<!DOCTYPE html><html lang="en"><head>
        <meta charset="UTF-8"> 
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <meta name="google-site-verification" content="tN1ANkxDkpFanVNXNfGs0pOFnDVAZH6tkBCRW2fkV8I">
        <!-- tocbot -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.11.1/tocbot.min.js"></script>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.11.1/tocbot.css">
        <!-- highlighting -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.1/styles/xcode.min.css">
        <!-- monaco editor -->
        <script src="monaco/vs/loader.js"></script>
        <script src="zapatos-bundle.js"></script>
        <!-- fonts -->
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
        <link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&amp;family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,700;1,8..60,400;1,8..60,700&amp;display=swap" rel="stylesheet">
        <!-- octocat -->
        <style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
        <!-- custom -->
        <link rel="stylesheet" href="docs.css">
      <title>Zapatos: Zero-Abstraction Postgres for TypeScript</title></head>
      <body>
        <!-- http://tholman.com/github-corners/ -->
        <a href="https://github.com/jawj/zapatos" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#aaa; color:#fff; position: fixed; z-index: 150; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>

        <div id="outer-toc">
          <div id="toc"></div>
          <div style="padding: 15px 20px;"><a href="https://github.com/jawj/zapatos">GitHub »</a></div>
        </div>
        <div id="content"><div class="logos"><img class="pg-logo" src="pg.svg" width="43.2" height="44.5" alt="Postgres logo"><img class="ts-logo" src="ts.svg" width="50.5" height="39" alt="TypeScript logo"></div>
<h1 id="zapatos-zero-abstraction-postgres-for-typescript"><b>Zap<span class="extra-vowels a">a</span>t<span class="extra-vowels o">o</span>s:</b> <br><span style="font-weight: normal;">Zero-Abstraction Postgres for TypeScript</span></h1>
<p><a href="https://www.postgresql.org/">Postgres</a> and <a href="https://www.typescriptlang.org/">TypeScript</a> are each, individually, fabulous.</p>
<p>Zapatos aims to make them work beautifully together. No abstractions, no distractions: just your database, with type safety.</p>
<div class="testimonials-window"><div class="testimonials">
<div class="testimonial">
<div class="quote">
<p>Wow this is amazing. […] Exactly the kind of ‘use SQL in typescript code with type-safety’ non-ORM that I’ve always wanted.</p>
</div>
<div class="attribution">
<p><a href="https://news.ycombinator.com/item?id=24371212">ummonk, HN</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>There are a number of TypeScript SQL libraries out there, but I found that Zapatos hits the sweet spot.</p>
</div>
<div class="attribution">
<p><a href="https://risticnikola.com/tips-for-apis-typescript">Nikola Ristić</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>Zapatos is amazing. […] I think its design is wonderful.</p>
</div>
<div class="attribution">
<p><a href="https://news.ycombinator.com/item?id=24367867">skrebbel, HN</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>Probably the most underrated #TypeScript #PostgreSQL package right now.</p>
</div>
<div class="attribution">
<p><a href="https://twitter.com/andywritescode/status/1265196222782070784">@andywritescode, Twitter</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>OK just ran the sample on my own schema, whoa, this is fire.</p>
</div>
<div class="attribution">
<p><a href="https://github.com/jawj/zapatos/issues/19#issuecomment-642740212">mrjjwright, GitHub</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>[I’ve] tried every ORM there is in the JS/TypeScript land. And I’ve settled on Zapatos, which […] is a breath of fresh air and is a delight to use.</p>
</div>
<div class="attribution">
<p><a href="https://news.ycombinator.com/item?id=27556821">moltar, HN</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>Am I crazy for thinking this seems really good?</p>
</div>
<div class="attribution">
<p><a href="https://twitter.com/hughevans/status/1295914249420550144">@hughevans, Twitter</a></p>
</div>
</div>
<div class="testimonial">
<div class="quote">
<p>Zapatos is super nice</p>
</div>
<div class="attribution">
<p><a href="https://news.ycombinator.com/item?id=26889128">nikolasburk (Prisma employee), HN</a></p>
</div>
</div>
</div></div>
<h2 id="what-does-it-do">What does it do?</h2>
<p>To achieve this aim, Zapatos does these five things:</p>
<ul>
<li>
<p><strong>Typescript schema</strong> &nbsp; A command-line tool speaks to your Postgres database and writes up a detailed TypeScript schema for every table. This is just a means to an end: it enables the next three things in this list. <a href="#typescript-schema">Show me »</a></p>
</li>
<li>
<p><strong>Arbitrary SQL</strong> &nbsp; Simple building blocks help you write arbitrary SQL using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates">tagged templates</a>, and manually apply the right types to what goes in and what comes back. <a href="#arbitrary-sql">Show me »</a></p>
</li>
<li>
<p><strong>Everyday CRUD</strong> &nbsp; Shortcut functions produce everyday <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> queries with no fuss and no surprises, fully and automatically typed. <a href="#everyday-crud">Show me »</a></p>
</li>
<li>
<p><strong>JOINs as nested JSON</strong> &nbsp; Nested shortcut calls generate <a href="https://www.postgresql.org/docs/12/queries-table-expressions.html#id-1.5.6.6.5.10.2"><code>LATERAL JOIN</code></a> queries, resulting in arbitrarily complex nested JSON structures, still fully and automatically typed. <a href="#joins-as-nested-json">Show me »</a></p>
</li>
<li>
<p><strong>Transactions</strong> &nbsp; Transaction helper functions assist in managing and retrying transactions. <a href="#transactions">Show me »</a></p>
</li>
</ul>
<h3 id="how-does-that-look">How does that look?</h3>
<h4 id="typescript-schema">Typescript schema</h4>
<p><strong>A command-line tool speaks to your Postgres database and writes up a detailed TypeScript schema for every table.</strong></p>
<p>Take this ultra-simple SQL schema for a single table, <code>authors</code>:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "authors" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "name" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "isLiving" <span class="hljs-type">BOOLEAN</span> );</span></code></pre>
<p>We run <code>npx zapatos</code> to generate a file named <code>schema.d.ts</code>, including table definitions like this one:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">namespace</span> authors {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">Table</span> = <span class="hljs-string">'authors'</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Selectable</span> {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">id</span>: <span class="hljs-built_in">number</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">isLiving</span>: <span class="hljs-built_in">boolean</span> | <span class="hljs-literal">null</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Whereable</span> {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    id?: <span class="hljs-built_in">number</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">number</span>&gt; | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    name?: <span class="hljs-built_in">string</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">string</span>&gt; | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    isLiving?: <span class="hljs-built_in">boolean</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">boolean</span>&gt; | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Insertable</span> {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    id?: <span class="hljs-built_in">number</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">number</span>&gt; | db.<span class="hljs-property">DefaultType</span> | db.<span class="hljs-property">SQLFragment</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">name</span>: <span class="hljs-built_in">string</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">string</span>&gt; | db.<span class="hljs-property">SQLFragment</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    isLiving?: <span class="hljs-built_in">boolean</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">boolean</span>&gt; | <span class="hljs-literal">null</span> | db.<span class="hljs-property">DefaultType</span> | db.<span class="hljs-property">SQLFragment</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Updatable</span> {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    id?: <span class="hljs-built_in">number</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">number</span>&gt; | db.<span class="hljs-property">DefaultType</span> | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    name?: <span class="hljs-built_in">string</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">string</span>&gt; | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    isLiving?: <span class="hljs-built_in">boolean</span> | db.<span class="hljs-property">Parameter</span>&lt;<span class="hljs-built_in">boolean</span>&gt; | <span class="hljs-literal">null</span> | db.<span class="hljs-property">DefaultType</span> | db.<span class="hljs-property">SQLFragment</span> <span class="hljs-comment">/* | ... etc ... */</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">/* ... etc ... */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<p>The type names are, I hope, reasonably self-explanatory. <code>authors.Selectable</code> is what I’ll get back from a <code>SELECT</code> query on this table. <code>authors.Whereable</code> is what I can use in a <code>WHERE</code> condition: everything’s optional, and I can include arbitrary SQL. <code>authors.Insertable</code> is what I can <code>INSERT</code>: it’s similar to the <code>Selectable</code>, but any fields that are <code>NULL</code>able and/or have <code>DEFAULT</code> values are allowed to be missing, <code>NULL</code> or <code>DEFAULT</code>. <code>authors.Updatable</code> is what I can <code>UPDATE</code> the table with: like what I can <code>INSERT</code>, but all columns are optional: it’s (roughly) a <code>Partial&lt;authors.Insertable&gt;</code>.</p>
<p><code>schema.d.ts</code> includes some other types that get used internally, including handy type mappings like this one:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">SelectableForTable</span>&lt;T <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Table</span>&gt; = {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">authors</span>: authors.<span class="hljs-property">Selectable</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">books</span>: books.<span class="hljs-property">Selectable</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">tags</span>: tags.<span class="hljs-property">Selectable</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">/* ... */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}[T];</span></code></pre>
<p>Zapatos supports tables, foreign tables, views and materialized views. It understands enumerated types: <code>CREATE TYPE "size" AS ENUM ('big', 'small');</code> comes to TypeScript as <code>'big' | 'small'</code>. And it lets you define the TypeScript treatment of <a href="https://www.postgresql.org/docs/current/domains.html">domain types</a> and user-defined types too.</p>
<p><a href="#how-do-i-get-it">Tell me more about the command line tool »</a></p>
<h4 id="arbitrary-sql">Arbitrary SQL</h4>
<p><strong>Simple building blocks help you write arbitrary SQL using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates">tagged templates</a>, and manually apply the right types to what goes in and what comes back.</strong></p>
<p>Let’s insert something into that <code>authors</code> table for which we just generated the types. We’ll write the SQL query ourselves, to show how that works (though we’ll see an easier way <a href="#everyday-crud">in the next section</a>):</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">author</span>: s.<span class="hljs-property">authors</span>.<span class="hljs-property">Insertable</span> = {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">name</span>: <span class="hljs-string">'Gabriel Garcia Marquez'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">isLiving</span>: <span class="hljs-literal">false</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [insertedAuthor] = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string">      INSERT INTO <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> (<span class="hljs-subst">${db.cols(author)}</span>)</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string">      VALUES (<span class="hljs-subst">${db.vals(author)}</span>) RETURNING *`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    .<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors" ("isLiving", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING <span class="hljs-operator">*</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>We apply the appropriate type to the object we’re trying to insert (<code>s.authors.Insertable</code>), giving us type-checking and autocompletion on that object. And we specify both which types are allowed as interpolated values in the template string (<code>s.authors.SQL</code>) and what type is going to be returned (<code>s.authors.Selectable[]</code>)&nbsp;when the query runs.</p>
<p>We also use the <a href="#cols-and-vals"><code>cols</code> and <code>vals</code> helper functions</a>. These compile, respectively, to the object’s keys (which are the column names) and query placeholders (<code>$1</code>, <code>$2</code>, …) for the corresponding values.</p>
<p><em>You can click ‘Explore types’ above to open the code in an embedded Monaco (VS Code) editor, so you can check those typings for yourself.</em></p>
<p><a href="#sql-tagged-template-strings">Tell me more about writing arbitrary SQL »</a></p>
<h4 id="everyday-crud">Everyday CRUD</h4>
<p><strong>Shortcut functions produce everyday <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> queries with no fuss and no surprises, fully and automatically typed.</strong></p>
<p>So —&nbsp;writing SQL with Zapatos is nicer than constructing a query and all its input and output types from scratch. But for a totally bog-standard CRUD query like the <code>INSERT</code> above, it still involves quite a lot of boilerplate.</p>
<p>To eliminate the boilerplate, Zapatos supplies some simple functions to generate these sorts of queries, fully and automatically typed.</p>
<p>Let’s use one of them — <code>insert</code> — to add two more authors:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [doug, janey] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'authors'</span>, [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Douglas Adams'</span>, <span class="hljs-attr">isLiving</span>: <span class="hljs-literal">false</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Jane Austen'</span>, <span class="hljs-attr">isLiving</span>: <span class="hljs-literal">false</span> },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors" ("isLiving", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The <code>insert</code> shortcut accepts a single <code>Insertable</code> or an <code>Insertable[]</code> array, and correspondingly returns a single <a href="#jsonselectable"><code>JSONSelectable</code></a> or a <code>JSONSelectable[]</code> array. Since we specified <code>'authors'</code> as the first argument here, and an array as the second, input and output will be checked and auto-completed as <code>authors.Insertable[]</code> and <code>authors.JSONSelectable[]</code> respectively.</p>
<p><em>Again, click ‘Explore types’ to play around and check those typings.</em></p>
<p>In addition to <code>insert</code>, there are shortcuts for <code>select</code> (plus <code>selectOne</code>, <code>selectExactlyOne</code>, and simple aggregates such as <code>count</code> and <code>sum</code>), and for <code>update</code>, <code>upsert</code>, <code>delete</code> and <code>truncate</code>.</p>
<p><a href="#shortcut-functions-and-lateral-joins">Tell me more about the shortcut functions »</a></p>
<h4 id="joins-as-nested-json">JOINs as nested JSON</h4>
<p><strong>Nested shortcut calls generate <a href="https://www.postgresql.org/docs/12/queries-table-expressions.html#id-1.5.6.6.5.10.2">LATERAL JOIN</a> queries, resulting in arbitrarily complex nested JSON structures, still fully and automatically typed.</strong></p>
<p>CRUD is our bread and butter, but the power of SQL is in the <code>JOIN</code>s. Postgres has powerful JSON features than can deliver sensibly-structured <code>JOIN</code> results with minimal post-processing: <code>json_agg</code>, <code>json_build_object</code>, and so on. Zapatos builds on these.</p>
<p>To demonstrate, let’s say that <code>authors</code> have <code>books</code> and <code>books</code> have <code>tags</code>, adding two new tables to our simple schema:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "books" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "authorId" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">REFERENCES</span> "authors"("id")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "title" TEXT</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "createdAt" TIMESTAMPTZ <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> now() );</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "tags"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "tag" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "bookId" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">REFERENCES</span> "books"("id") <span class="hljs-keyword">ON</span> <span class="hljs-keyword">DELETE</span> CASCADE );</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">UNIQUE</span> INDEX "tagsUniqueIdx" <span class="hljs-keyword">ON</span> "tags"("tag", "bookId");</span></code></pre>
<p>And let’s say I want to show a list of books, each with its (one) author and (many) associated tags. We could knock up a manual query for this, of course, but <a href="#manual-joins-using-postgres-json-features">it gets quite hairy</a>. The <code>select</code> shortcut has an option called <code>lateral</code> that can nest other <code>select</code> queries and do it for us.</p>
<p>Let’s try it:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> bookAuthorTags = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">author</span>: db.<span class="hljs-title function_">selectExactlyOne</span>(<span class="hljs-string">'authors'</span>, { <span class="hljs-attr">id</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'authorId'</span>) }),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">tags</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'tags'</span>, { <span class="hljs-attr">bookId</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'id'</span>) }),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "lateral_author".<span class="hljs-keyword">result</span>, $<span class="hljs-number">2</span>::text, "lateral_tags".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> "books"."authorId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  LIMIT $<span class="hljs-number">3</span>) <span class="hljs-keyword">AS</span> "lateral_author" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">SELECT</span> to_jsonb ("tags".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">FROM</span> "tags"</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHERE</span> ("bookId" <span class="hljs-operator">=</span> "books"."id")) <span class="hljs-keyword">AS</span> "sq_tags") <span class="hljs-keyword">AS</span> "lateral_tags" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"author"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"tags"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"3/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"mystery"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"adventure"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Holes"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605559+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>This generates an efficient three-table <code>LATERAL JOIN</code> that returns a nested JSON structure directly from the database. Every nested element is again fully and automatically typed.</p>
<p><em>Again, you can click ‘Explore types’ above to open the code in an embedded Monaco (VS Code) editor, so you can check those typings for yourself.</em></p>
<p>We can of course extend this to deeper nesting (e.g. query each author, with their books, with their tags); to self-joins (of a table with itself, e.g. employees to their managers in the same <code>employees</code> table); and to joins on relationships other than foreign keys (e.g. joining the nearest <em>N</em> somethings using the PostGIS <code>&lt;-&gt;</code> distance operator).</p>
<p><a href="#lateral-and-alias">Tell me more about nested <code>select</code> queries »</a></p>
<h4 id="transactions">Transactions</h4>
<p><strong>Transaction helper functions assist in managing and retrying transactions.</strong></p>
<p>Transactions are where I’ve found traditional ORMs like TypeORM and Sequelize most footgun-prone. Zapatos is always explicit about what client or pool is running your query —&nbsp;hence that <code>pool</code> argument in all our examples so far.</p>
<p>Zapatos also offers simple transaction helpers that handle issuing a SQL <code>ROLLBACK</code> on error, releasing the database client in a <code>finally</code> clause, and automatically retrying queries in case of serialization failures. There’s one for each isolation level (<code>SERIALIZABLE</code>, <code>REPEATABLE READ</code>, and so on), and they look like this:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-keyword">async</span> txnClient =&gt; {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">/* queries here use txnClient instead of pool */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">});</span></code></pre>
<p>For instance, take this <code>bankAccounts</code> table:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "bankAccounts" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "balance" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span> <span class="hljs-keyword">CHECK</span> ("balance" <span class="hljs-operator">&gt;=</span> <span class="hljs-number">0</span>) );</span></code></pre>
<p>We can use the transaction helpers like so:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [accountA, accountB] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'bankAccounts'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [{ <span class="hljs-attr">balance</span>: <span class="hljs-number">50</span> }, { <span class="hljs-attr">balance</span>: <span class="hljs-number">50</span> }]).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-title function_">transferMoney</span> = (<span class="hljs-params">sendingAccountId: <span class="hljs-built_in">number</span>, receivingAccountId: <span class="hljs-built_in">number</span>, amount: <span class="hljs-built_in">number</span></span>) =&gt;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-function"><span class="hljs-params">txnClient</span> =&gt;</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    db.<span class="hljs-title function_">update</span>(<span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">balance</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> - <span class="hljs-subst">${db.param(amount)}</span>`</span> },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">id</span>: sendingAccountId }).<span class="hljs-title function_">run</span>(txnClient),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    db.<span class="hljs-title function_">update</span>(<span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">balance</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> + <span class="hljs-subst">${db.param(amount)}</span>`</span> },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">id</span>: receivingAccountId }).<span class="hljs-title function_">run</span>(txnClient),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]));</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> [[updatedAccountA], [updatedAccountB]] = <span class="hljs-keyword">await</span> <span class="hljs-title function_">transferMoney</span>(accountA.<span class="hljs-property">id</span>, accountB.<span class="hljs-property">id</span>, <span class="hljs-number">60</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">} <span class="hljs-keyword">catch</span>(<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">message</span>, <span class="hljs-string">'/'</span>, err.<span class="hljs-property">detail</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "bankAccounts" ("balance")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>), ($<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">50</span><span class="hljs-punctuation">,</span> <span class="hljs-number">50</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">-</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">60</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">+</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">60</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">new row for relation "bankAccounts" violates check constraint "bankAccounts_balance_check" / Failing row contains (1, -10).</span></code></pre>
</div>
<p>Finally, Zapatos provides a set of hierarchical isolation types so that, for example, if you type a <code>txnClient</code> argument to a function as <code>TxnClientForRepeatableRead</code>, you can call it with <code>IsolationLevel.Serializable</code> or <code>IsolationLevel.RepeatableRead</code> but not <code>IsolationLevel.ReadCommitted</code>.</p>
<p><a href="#transaction">Tell me more about the transaction functions »</a></p>
<h3 id="why-does-it-do-those-things">Why does it do those things?</h3>
<p>It is a truth universally acknowledged that <a href="https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch">ORMs aren’t very good</a>. JavaScript and TypeScript ORMs are perhaps even worse than the average. One Zapatos user <a href="https://news.ycombinator.com/item?id=27556821">described a popular TypeScript ORM</a> as “full of broken magic under the hood”, which nicely captures what originally motivated me to write this library.</p>
<p>I like SQL, and Postgres especially. In my experience, abstractions that obscure the underlying SQL, or that prioritise ease of switching to another database tomorrow over effective use of <em>this</em> database <em>today</em>, are a source of misery.</p>
<p>I’ve also come to love strongly typed languages, and TypeScript in particular. VS Code’s type checking and autocomplete speed development, prevent bugs, and simplify refactoring. Especially when they <em>just happen</em>, they bring joy. But, traditionally, talking to the database is a place where they really don’t <em>just happen</em>.</p>
<p>Zapatos aims to fix that.</p>
<p>If it interests you, there’s a whole other <a href="https://github.com/jawj/mostly-ormless">repository about how Zapatos came about</a>.</p>
<h3 id="what-doesnt-it-do">What doesn’t it do?</h3>
<p>Zapatos doesn’t handle schema migrations. Other tools can help you with this: check out <a href="https://github.com/amacneil/dbmate">dbmate</a>, for instance.</p>
<p>It also doesn’t manage the connection pool for you, as some ORMs do — mainly because the <code>pg</code> module makes this so easy. For example, my <code>pgPool.ts</code> looks something like this:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> pg.<span class="hljs-title class_">Pool</span>({ <span class="hljs-attr">connectionString</span>: process.<span class="hljs-property">env</span>.<span class="hljs-property">DATABASE_URL</span> });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">pool.<span class="hljs-title function_">on</span>(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(err));  <span class="hljs-comment">// don't let a pg restart kill your app</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> pool;</span></code></pre>
<p>Finally, it won’t tell you how to structure your code: Zapatos doesn’t deal in the ‘model’ classes beloved of traditional ORMs, just (fully-typed) <a href="https://twitter.com/_ericelliott/status/831965087749533698?lang=en">POJOs</a>.</p>
<h2 id="how-do-i-get-it">How do I get it?</h2>
<h3 id="install-it">Install it</h3>
<p>First: check your <code>tsconfig.json</code>. You need <code>"strictNullChecks": true</code> or <code>"strict": true</code> (which implies <code>"strictNullChecks": true</code>). Without <code>strictNullChecks</code>, some things just won’t work —&nbsp;namely, the <code>lateral</code>, <code>extras</code>, <code>returning</code> and <code>columns</code> options to the shortcut functions.</p>
<p>Since TypeScript 4.4, it’s <a href="https://github.com/jawj/zapatos/issues/97">also a good idea to set <code>"exactOptionalPropertyTypes": true</code></a>.</p>
<p>Then install Zapatos with <code>npm</code>:</p>
<pre class="language-bash"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">npm install --save zapatos</span></code></pre>
<h3 id="configure-it">Configure it</h3>
<p>Add a top-level file <code>zapatosconfig.json</code> to your project. Here’s an example:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"db"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"connectionString"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"postgresql://localhost/example_db"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"outDir"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"./src"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<p>These are available top-level keys, all of which are optional:</p>
<ul>
<li>
<p><code>"db"</code> gives Postgres connection details. You can provide <a href="https://node-postgres.com/features/connecting/#Programmatic">anything that you’d pass</a> to <code>new pg.Pool(/* ... */)</code> here.</p>
</li>
<li>
<p><code>"outDir"</code> defines where your <code>zapatos</code> folder will be created, relative to the project root. If not specified, it defaults to the project root, i.e. <code>"."</code>.</p>
</li>
<li>
<p><code>"outExt"</code> defines the file extension for all generated type files. It defaults to <code>".d.ts"</code>, but <a href="https://github.com/jawj/zapatos/issues/53">for certain use cases you may wish to set it to <code>".ts"</code></a>.</p>
</li>
<li>
<p><code>"progressListener"</code> is a boolean that determines how chatty the tool is. If <code>true</code>, it enumerates its progress in generating the schema. It defaults to <code>false</code>. If you <a href="#programmatic-generation">generate your schema programmatically</a>, you can alternatively provide your own listener function.</p>
</li>
<li>
<p><code>"warningListener"</code> is a boolean that determines whether or not the tool logs a warning when a new user-defined type or domain is encountered and given its own type file in <code>zapatos/custom</code>. If <code>true</code>, which is the default, it does. Again, if you <a href="#programmatic-generation">generate your schema programmatically</a>, you can alternatively provide your own listener function.</p>
</li>
<li>
<p><code>"customTypesTransform"</code> is a string that determines how user-defined Postgres type names are mapped to TypeScript type names. Your options are <code>"my_type"</code>, <code>"PgMyType"</code> or <code>"PgMy_type"</code>, each&nbsp;representing how a Postgres type named <code>my_type</code> will be transformed. The default (for reasons of backward-compatibility rather than superiority) is <code>"PgMy_type"</code>. If you <a href="#programmatic-generation">generate your schema programmatically</a>, you can alternatively define your own transformation function.</p>
</li>
<li>
<p><code>"schemas"</code> is an object that lets you define the schemas, and the tables and views within schemas, for which types will be generated. Each key is a schema name, and each value is an object with keys <code>"include"</code> and <code>"exclude"</code>. Those keys can take the value <code>"*"</code> (for all tables in the schema) or an array of table names. The <code>"exclude"</code> list takes precedence over the <code>"include"</code> list. Thanks to generous sponsorship by <a href="https://www.seam.co/">Seam</a>, schemas are <a href="https://github.com/jawj/zapatos/issues/3#issuecomment-1126933350">properly supported</a> (via namespacing of types) as of version 6.</p>
</li>
</ul>
<p>If not specified, the default value for <code>"schemas"</code> includes all tables in the <code>public</code> schema, i.e.:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-attr">"schemas"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"public"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"include"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"*"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"exclude"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<p>If you use PostGIS, you’ll likely want to exclude its system tables:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-attr">"schemas"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"public"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"include"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"*"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"exclude"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">"geography_columns"</span><span class="hljs-punctuation">,</span> </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">"geometry_columns"</span><span class="hljs-punctuation">,</span> </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">"raster_columns"</span><span class="hljs-punctuation">,</span> </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">"raster_overviews"</span><span class="hljs-punctuation">,</span> </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">"spatial_ref_sys"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<ul>
<li>
<p><code>"unprefixedSchema"</code> determines which schema’s objects don’t need to be prefixed with their schema name (so that you can specify table <code>myTable</code> rather than <code>public.myTable</code>, for example). It should be set to the first schema listed in your Postgres <code>search_path</code> that actually exists in the database. Usually, that’s <code>"public"</code>, which is the option’s default value. <code>"unprefixedSchema"</code> can also be set to <code>null</code>, in which case all objects will be prefixed. That’s necessary if any schema shares its name with any table in the <code>public</code> schema.</p>
</li>
<li>
<p><code>"columnOptions"</code> is an object mapping options to named columns of named (or all) tables. Currently, you can use it to manually exclude column keys from the <code>Insertable</code> and <code>Updatable</code> types, using the options <code>"insert": "excluded"</code> and <code>"update": "excluded"</code>, or to force column keys to be optional in <code>Insertable</code> types, using the option <code>"insert": "optional"</code>. This supports use cases where columns are set using triggers.</p>
</li>
</ul>
<p>For example, say you have a <code>BEFORE INSERT</code> trigger on your <code>customers</code> table that can guess a default value for the <code>gender</code> column based on the value of the <code>title</code> column (though note: <a href="https://design-system.service.gov.uk/patterns/gender-or-sex/">don’t do that</a>). In this case, the <code>gender</code> column is actually optional on insert, even if it’s <code>NOT NULL</code> with no default, because the trigger provides a default value. You can tell Zapatos about that like so:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-attr">"columnOptions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"customers"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"gender"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"insert"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"optional"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<p>Note that tables outside the <code>public</code> schema (or whichever schema you set for <code>"unprefixedSchema"</code>) should be schema-prefixed here, as usual —&nbsp;e.g. <code>"columnOptions": { "someSchema.someTable": /* ... */ } }</code>.</p>
<p>You can also use <code>"*"</code> as a wildcard to match all tables in all schemas. For example, perhaps you’ve set up the appropriate triggers to keep <code>updatedAt</code> columns up to date throughout your database. Then you might choose to exclude all your <code>updatedAt</code> columns from the <code>Insertable</code> and <code>Updatable</code> types for all tables as follows:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-attr">"columnOptions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"*"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"updatedAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"insert"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"excluded"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"update"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"excluded"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<p>Wildcard table options have lower precedence than named table options. The default values, should you want to restore them for named tables, are <code>"insert": "auto"</code> and <code>"update": "auto"</code>. Note that <code>"*"</code> is only supported as the whole key — you can’t use a <code>*</code> to match parts of schema or table names — and isn’t supported for column names.</p>
<ul>
<li>
<p><code>"schemaJSDoc"</code> is a boolean that turns JSDoc comments for each column in the generated schema on (the default) or off. JSDoc comments enable per-column VS Code pop-ups giving details of Postgres data type, default value and so on. They also make the schema file longer and less readable.</p>
</li>
<li>
<p><code>"customJSONParsingForLargeNumbers"</code> is a boolean that changes the types for <code>bigint</code>/<code>int8</code> and <code>numeric</code>/<code>decimal</code> values to reflect the use of <a href="#custom-json-parsing-for-bigint-and-numeric">custom JSON parsing to maintain precision</a>.</p>
</li>
</ul>
<p>In summary, the expected structure is defined like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">OptionalConfig</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">db</span>: pg.<span class="hljs-property">ClientConfig</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">outDir</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">outExt</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">schemas</span>: <span class="hljs-title class_">SchemaRules</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">unprefixedSchema</span>: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">progressListener</span>: <span class="hljs-built_in">boolean</span> | (<span class="hljs-function">(<span class="hljs-params">s: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">warningListener</span>: <span class="hljs-built_in">boolean</span> | (<span class="hljs-function">(<span class="hljs-params">s: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">customTypesTransform</span>: <span class="hljs-string">'PgMy_type'</span> | <span class="hljs-string">'my_type'</span> | <span class="hljs-string">'PgMyType'</span> | (<span class="hljs-function">(<span class="hljs-params">s: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">string</span>);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">columnOptions</span>: <span class="hljs-title class_">ColumnOptions</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">schemaJSDoc</span>: <span class="hljs-built_in">boolean</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">customJSONParsingForLargeNumbers</span>: <span class="hljs-built_in">boolean</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">SchemaRules</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [<span class="hljs-attr">schema</span>: <span class="hljs-built_in">string</span>]: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">include</span>: <span class="hljs-string">'*'</span> | <span class="hljs-built_in">string</span>[];</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">exclude</span>: <span class="hljs-string">'*'</span> | <span class="hljs-built_in">string</span>[];</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">ColumnOptions</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [<span class="hljs-attr">k</span>: <span class="hljs-built_in">string</span>]: {  <span class="hljs-comment">// table name or '*'</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    [<span class="hljs-attr">k</span>: <span class="hljs-built_in">string</span>]: {  <span class="hljs-comment">// column name</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      insert?: <span class="hljs-string">'auto'</span> | <span class="hljs-string">'excluded'</span> | <span class="hljs-string">'optional'</span>;</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      update?: <span class="hljs-string">'auto'</span> | <span class="hljs-string">'excluded'</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    };</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<h4 id="environment-variables">Environment variables</h4>
<p>All values in <code>zapatosconfig.json</code> can have environment variables (Node’s <code>process.env.SOMETHING</code>) interpolated via <a href="https://handlebarsjs.com/">handlebars</a>-style doubly-curly-brackets <code>{{variables}}</code>.</p>
<p>This is likely most useful for the database connection details. For example, on Heroku you might configure your database as:</p>
<pre class="language-json"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-attr">"db"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"connectionString"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"{{DATABASE_URL}}"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<h4 id="eslint--tslint">ESLint / tslint</h4>
<p>A general configuration suggestion: set up <a href="https://typescript-eslint.io/getting-started/">ESLint</a> with the rules <a href="https://typescript-eslint.io/rules/await-thenable/"><code>@typescript-eslint/await-thenable</code></a> and <a href="https://typescript-eslint.io/rules/no-floating-promises/"><code>@typescript-eslint/no-floating-promises</code></a> (or the now-deprecated <a href="https://palantir.github.io/tslint/">tslint</a> with <a href="https://palantir.github.io/tslint/rules/no-floating-promises/"><code>no-floating-promises</code></a> and <a href="https://palantir.github.io/tslint/rules/await-promise/"><code>await-promise</code></a>) to avoid various <code>Promise</code>-related pitfalls.</p>
<h3 id="generate-your-schema">Generate your schema</h3>
<p>Zapatos provides a command line tool. With everything configured, run it like so:</p>
<pre><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">npx zapatos</span></code></pre>
<p>This generates the TypeScript schema for your database as <code>zapatos/schema.d.ts</code> inside your configured <code>outDir</code>. Any user-defined or domain types encountered get defined within <code>zapatos/custom</code> in their own <code>.d.ts</code> files, which you can subsequently customise.</p>
<p>These files must be included in your TypeScript compilation. That may happen for you automatically, but you may need to check the <code>"include"</code> or <code>"files"</code> keys in <code>tsconfig.json</code>. If you use <code>ts-node</code> or <code>node -r ts-node/register</code>, you may need to change it to <code>ts-node --files</code> or set <code>TS_NODE_FILES=true</code>.</p>
<h4 id="programmatic-generation">Programmatic generation</h4>
<p>As an alternative to the command line tool, it’s also possible to generate the schema programmatically by importing from <code>zapatos/generate</code>. For example:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> zg <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/generate'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-attr">zapCfg</span>: zg.<span class="hljs-property">Config</span> = { <span class="hljs-attr">db</span>: { <span class="hljs-attr">connectionString</span>: <span class="hljs-string">'postgres://localhost/mydb'</span> } };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> zg.<span class="hljs-title function_">generate</span>(zapCfg);</span></code></pre>
<p>Call the <code>generate</code> method with an object structured exactly the same as <code>zapatosconfig.json</code>, documented above, with the following two exceptions:</p>
<ul>
<li>
<p>The <code>"progressListener"</code> and <code>"warningListener"</code> keys can each take <code>true</code> or <code>false</code> (as in the JSON case), or alternatively a function with the signature <code>(s: string) =&gt; void</code>, which you can use to implement your own logging.</p>
</li>
<li>
<p>The <code>"customTypesTransform"</code> key can take any of the string values allowed in the JSON case, or otherwise a function with the signature <code>(s: string) =&gt; string</code>, with which you can define your own type name transformation.</p>
</li>
</ul>
<h4 id="custom-types-and-domains">Custom types and domains</h4>
<p>As mentioned previously, any user-defined or domain types encountered during schema generation get defined in their own <code>.d.ts</code> files under <code>zapatos/custom</code>, which you can subsequently customise.</p>
<p>You can use domain types in order to specify custom types on the TypeScript side for certain Postgres columns. Say, for example, that you have a Postgres <code>jsonb</code> column on which you want to impose a particular structure. You could do the following:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> DOMAIN "mySpecialJsonb" <span class="hljs-keyword">AS</span> "jsonb";</span></code></pre>
<p>Since you’ve done nothing else with this domain, it’s effectively just a simple alias to <code>jsonb</code> on the Postgres side. Now you can use that in place of <code>jsonb</code> in your table definition:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> "myTable" <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">COLUMN</span> "myExistingJsonbColumn" TYPE "mySpecialJsonb";</span></code></pre>
<p>When you next regenerate the TypeScript schema, you’ll find a custom type for <code>PgMySpecialJsonb</code> in <code>zapatos/custom/PgMySpecialJsonb.d.ts</code>, defined like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">PgMySpecialJsonb</span> = db.<span class="hljs-property">JSONValue</span>;</span></code></pre>
<p>You can of course replace this definition with whatever TypeScript type or interface you choose. The file will not be overwritten on future schema generations. For example, perhaps this column holds blog article data:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">PgMySpecialJsonb</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">title</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">text</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">tags</span>: <span class="hljs-built_in">string</span>[];</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">version</span>: <span class="hljs-built_in">number</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">};</span></code></pre>
<h3 id="import-it">Import it</h3>
<p>In your code, get the core library like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span></code></pre>
<p>ESM wrappers are provided, so the import should work the same whether your project is set to use the CommonJS or ESM module specs.</p>
<p>To import your ordinary schema types (<code>myTable.Selectable</code>, <code>myOtherTable.Insertable</code>, etc.):</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span></code></pre>
<p>Be sure to <code>import type</code> for this, not plain <code>import</code>, or you’ll upset <code>ts-jest</code> and maybe others.</p>
<p>To import any user-defined or domain types:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> * <span class="hljs-keyword">as</span> c <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/custom'</span>;</span></code></pre>
<p>The paths <code>zapatos/db</code> and <code>zapatos/generate</code> point to real folders in <code>node_modules</code>. Although they look like file paths, <code>zapatos/schema</code> and <code>zapatos/custom</code> are actually the names of <a href="https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules">ambient modules</a> declared in the generated files in your source tree: <code>zapatos/schema.d.ts</code> and <code>zapatos/custom/*.d.ts</code>.</p>
<h2 id="user-guide">User guide</h2>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/core.ts#L210">Source code »</a></div>
<h3 id="sql-tagged-template-strings"><code>sql</code> tagged template strings</h3>
<p>Arbitrary queries are written using the tagged template function <code>sql</code>, which returns <a href="#sqlfragment"><code>SQLFragment</code></a> class instances.</p>
<p>The <code>sql</code> function is <a href="https://www.typescriptlang.org/docs/handbook/generics.html">generic</a>, having two type variables. For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authors = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The first type variable, <code>Interpolations</code> (above: <code>s.authors.SQL</code>), defines allowable interpolation values. If not specified, it defaults to <code>db.SQL</code>: this is the union of all the per-table <code>SQL</code> types, and thus allows all table and column names present in the database as string interpolations (some of which would throw runtime errors in this case).</p>
<p>As another example, imagine we were joining the <code>authors</code> and <code>books</code> tables. Then we could specify <code>s.authors.SQL | s.books.SQL</code> for <code>Interpolations</code> here.</p>
<p>The second type variable, <code>RunResult</code> (above: <code>s.authors.Selectable[]</code>), describes what will be returned if we call <code>run()</code> on the query (after any transformations performed in <a href="#runresulttransform-qr-pgqueryresult--any"><code>runResultTransform()</code></a>), or if we embed it within the <a href="#extras"><code>extras</code></a> or <a href="#lateral-and-alias"><code>lateral</code></a> query options. Its default value if not specified is <code>any[]</code>.</p>
<p>Take another example of these type variables:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [{ random }] = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;<span class="hljs-built_in">never</span>, [{ <span class="hljs-attr">random</span>: <span class="hljs-built_in">number</span> }]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT random()`</span>.<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(random);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> random()</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"random"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0.621588923150834</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">0.621588923150834</span></code></pre>
</div>
<p><code>Interpolations</code> is <code>never</code> because nothing needs to be interpolated in this query, and the <code>RunResult</code> type says that the query will return one row comprising one numeric column, named <code>random</code>. The <code>random</code> TypeScript variable we initialize will of course be typed as a <code>number</code>.</p>
<p>If you’re happy to have your types tied down a little less tightly, it also works to wholly omit the type variables in this particular query, falling back on their defaults:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [{ random }] = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span><span class="hljs-string">`SELECT random()`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre>
<p>In this case, the <code>random</code> variable is of course still a <code>number</code>, but it is typed as <code>any</code>.</p>
<h3 id="sql-template-interpolation-types"><code>sql</code> template interpolation types</h3>
<h4 id="strings">Strings</h4>
<p>The strings that can be directly interpolated into a <code>sql</code> template string are defined by its <code>Interpolations</code> type variable, <a href="#sql-tagged-template-strings">as noted above</a>. Typically, this will limit them to the names of tables and columns.</p>
<p>Interpolated strings are passed through to the raw SQL query double-quoted,&nbsp;to preserve capitalisation and neutralise SQL keywords. For example, <code>myTable</code> becomes <code>"myTable"</code>, and <code>mySchema.myTable</code> becomes <code>"mySchema"."myTable"</code>.</p>
<p>It’s highly preferable to use interpolated string literals for table and column names rather than just writing those values in the query itself, in order to benefit from auto-completion and (ongoing) type-checking.</p>
<p>So, for example, do write:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> title = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span><span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT <span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span> FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> LIMIT 1`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre>
<p>But <strong>don’t</strong> write</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> title = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span><span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT "title" FROM "books" LIMIT 1`</span>.<span class="hljs-title function_">run</span>(pool);  <span class="hljs-comment">// no, don't do this</span></span></code></pre>
<p>—&nbsp;even if the two produce the same result right now.</p>
<p>More critically, <strong>never never never</strong> explicitly override type-checking so as to write:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  nameSubmittedByUser = <span class="hljs-string">'books"; DROP TABLE "authors"; --'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  title = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;<span class="hljs-built_in">any</span>&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${nameSubmittedByUser}</span> LIMIT 1`</span>.<span class="hljs-title function_">run</span>(pool);  <span class="hljs-comment">// NEVER do this!</span></span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books";</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLE</span> "authors";</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">--" LIMIT 1</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">error: cannot drop table authors because other objects depend on it</span></code></pre>
</div>
<p>If you override type-checking to pass untrusted data to Zapatos in unexpected places, such as the above use of <code>any</code>, you can expect successful SQL injection attacks.</p>
<p>(It <em>is</em> safe to pass untrusted data as values in <code>Whereable</code>, <code>Insertable</code>, and <code>Updatable</code> objects, manually by using <a href="#paramvalue-any-cast-boolean--string-parameter"><code>param</code></a>, and in certain other places. If you’re in any doubt, double-check that the generated SQL is using <code>$1</code>, <code>$2</code>, … parameters for all potentially untrusted data).</p>
<h4 id="cols-and-vals"><code>cols()</code> and <code>vals()</code></h4>
<p>The <code>cols</code> and <code>vals</code> wrapper functions (which return <code>ColumnNames</code> and <code>ColumnValues</code> class instances respectively) are intended to help with certain <code>INSERT</code> and <code>SELECT</code> queries.</p>
<p>In the <code>INSERT</code> context, pass them each the same <code>Insertable</code> object: <code>cols</code> is compiled to a comma-separated list of the object’s keys, which are the column names, and <code>vals</code> is compiled to a comma-separated list of SQL placeholders (<code>$1</code>, <code>$2</code>, …) associated with the corresponding values, in matching order. To return to (approximately) an earlier example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">author</span>: s.<span class="hljs-property">authors</span>.<span class="hljs-property">Insertable</span> = {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">name</span>: <span class="hljs-string">'Joseph Conrad'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">isLiving</span>: <span class="hljs-literal">false</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [insertedAuthor] = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    INSERT INTO <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> (<span class="hljs-subst">${db.cols(author)}</span>)</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    VALUES (<span class="hljs-subst">${db.vals(author)}</span>) RETURNING *`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors" ("isLiving", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING <span class="hljs-operator">*</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The <code>cols</code> and <code>vals</code> wrappers can also each take an array instead of an object.</p>
<p>For the <code>cols</code> function, this can help us select only a subset of columns, in conjunction with the <code>OnlyCols</code> type. Pass an array of column names to <code>cols</code> to have them compiled appropriately, as seen in this example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// the &lt;const&gt; prevents generalization to string[]</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> bookCols = &lt;<span class="hljs-keyword">const</span>&gt;[<span class="hljs-string">'id'</span>, <span class="hljs-string">'title'</span>];</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> <span class="hljs-title class_">BookDatum</span> = s.<span class="hljs-property">books</span>.<span class="hljs-property">OnlyCols</span>&lt;<span class="hljs-keyword">typeof</span> bookCols&gt;;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  bookData = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, <span class="hljs-title class_">BookDatum</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT <span class="hljs-subst">${db.cols(bookCols)}</span> FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> "id", "title"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Holes"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>For the <code>vals</code> function, this can help with <code>IN (...)</code> queries, such as the following:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  authorIds = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">123</span>],</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  authors = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">` </span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> WHERE <span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span> IN (<span class="hljs-subst">${db.vals(authorIds)}</span>)`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> "id" <span class="hljs-keyword">IN</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>, $<span class="hljs-number">3</span>)</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-number">123</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<h4 id="whereable"><code>Whereable</code></h4>
<p>Any plain JavaScript object interpolated into a <code>sql</code> template string is type-checked as a <code>Whereable</code>, and compiled into one or more conditions joined with <code>AND</code> (but, for flexibility, no <code>WHERE</code>). The object’s keys represent column names, and the corresponding values are automatically compiled as (injection-safe) <a href="#paramvalue-any-cast-boolean--string-parameter"><code>Parameter</code></a> instances.</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  title = <span class="hljs-string">'Northern Lights'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> WHERE <span class="hljs-subst">${{ title }}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("title" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>(If you need to specify a <code>CAST</code> of a parameter to a specific SQL type, you can also manually wrap <code>Whereable</code> values using <a href="#paramvalue-any-cast-boolean--string-parameter"><code>param</code></a> —&nbsp;this is useful primarily when using <a href="#shortcut-functions-and-lateral-joins">the shortcut functions</a>).</p>
<p>A <code>Whereable</code>’s values can alternatively be <code>SQLFragments</code>, and this makes them extremely flexible. In a <code>SQLFragment</code> inside a <code>Whereable</code>, the special symbol <code>self</code> can be used to refer to the column name. This arrangement enables us to use any operator or function we want — not just <code>=</code>.</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  titleLike = <span class="hljs-string">'Northern%'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> WHERE <span class="hljs-subst">${{ </span></span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string"><span class="hljs-subst">      title: db.sql<span class="hljs-string">`<span class="hljs-subst">${db.self}</span> LIKE <span class="hljs-subst">${db.param(titleLike)}</span>`</span>,</span></span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string"><span class="hljs-subst">      createdAt: db.sql<span class="hljs-string">`<span class="hljs-subst">${db.self}</span> &gt; now() - INTERVAL '7 days'`</span>,</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string"><span class="hljs-subst">    }}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> (("createdAt" <span class="hljs-operator">&gt;</span> now() <span class="hljs-operator">-</span> <span class="hljs-type">INTERVAL</span> <span class="hljs-string">'7 days'</span>)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">AND</span> ("title" <span class="hljs-keyword">LIKE</span> $<span class="hljs-number">1</span>))</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Northern%"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Finally, there’s a set of helper functions you can use to create appropriate <code>SQLFragment</code>s like these for use as <code>Whereable</code> values. The advantages are: (1) there’s slighly less to type, and (2) you get type-checking on their arguments (so you’re not tempted to compare incomparable things).</p>
<p>They’re exported under <code>conditions</code> on the main object, and the full set can be seen in <a href="https://github.com/jawj/zapatos/blob/master/src/db/conditions.ts">conditions.ts</a>. Using some of these, we could rewrite the above example as:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  titleLike = <span class="hljs-string">'Northern%'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> WHERE <span class="hljs-subst">${{ </span></span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string"><span class="hljs-subst">      title: dc.like(titleLike),</span></span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string"><span class="hljs-subst">      createdAt: dc.after(dc.fromNow(-<span class="hljs-number">7</span>, <span class="hljs-string">'days'</span>)),</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string"><span class="hljs-subst">    }}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> (("createdAt" <span class="hljs-operator">&gt;</span> now() <span class="hljs-operator">+</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">AND</span> ("title" <span class="hljs-keyword">LIKE</span> $<span class="hljs-number">2</span>))</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"-7 days"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Northern%"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<h4 id="self"><code>self</code></h4>
<p>The use of the <code>self</code> symbol is explained in <a href="#whereable">the section on <code>Whereable</code>s</a>.</p>
<h4 id="paramvalue-any-cast-boolean--string-parameter"><code>param(value: any, cast?: boolean | string): Parameter</code></h4>
<p>In general, Zapatos’ type-checking won’t let us <a href="https://xkcd.com/327/">pass user-supplied data unsafely into a query</a> by accident. The <code>param</code> wrapper function exists to enable the safe passing of user-supplied data into a query using numbered query parameters (<code>$1</code>, <code>$2</code>, …).</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  title = <span class="hljs-string">'Pride and Prejudice'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> WHERE <span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span> = <span class="hljs-subst">${db.param(title)}</span>`</span>.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> "title" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Pride and Prejudice"</span><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>This same mechanism is applied automatically when we use <a href="#whereable">a <code>Whereable</code> object</a> (and in this example, using a <code>Whereable</code> would be more readable and more concise). It’s also applied when we use <a href="#cols-and-vals">the <code>vals</code> function</a> to create a <code>ColumnValues</code> wrapper object.</p>
<p>The optional second argument to <code>param</code>, <code>cast</code>, allows us to specify a SQL <code>CAST</code> type for the wrapped value. If <code>cast</code> is a string, it’s interpreted as a Postgres type, so <code>param(someValue, 'text')</code> comes out in the compiled query as as <code>CAST($1 TO "text")</code>. If <code>cast</code> is <code>true</code>, the parameter value will be JSON stringified and cast to <code>json</code>, and if <code>cast</code> is <code>false</code>, the parameter will <strong>not</strong> be JSON stringified or cast to <code>json</code> (regardless, in both cases, of <a href="#casting-parameters-to-json">the <code>castArrayParamsToJson</code> and <code>castObjectParamsToJson</code> configuration options</a>).</p>
<h4 id="default"><code>Default</code></h4>
<p>The <code>Default</code> symbol simply compiles to the SQL <code>DEFAULT</code> keyword. This may be useful in <code>INSERT</code> and <code>UPDATE</code> queries where no value is supplied for one or more of the affected columns.</p>
<h4 id="sql-template-strings"><code>sql</code> template strings</h4>
<p><code>sql</code> template strings (resulting in <code>SQLFragment</code>s) can be interpolated within other <code>sql</code> template strings (<code>SQLFragment</code>s). This provides flexibility in building queries programmatically.</p>
<p>For example, the <a href="#select-selectone-and-selectexactlyone"><code>select</code> shortcut</a> makes extensive use of nested <code>sql</code> templates to build its queries:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  rowsQuery = sql&lt;<span class="hljs-variable constant_">SQL</span>, <span class="hljs-built_in">any</span>&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT <span class="hljs-subst">${allColsSQL}</span> AS result </span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    FROM <span class="hljs-subst">${table}</span><span class="hljs-subst">${tableAliasSQL}</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    <span class="hljs-subst">${lateralSQL}</span><span class="hljs-subst">${whereSQL}</span><span class="hljs-subst">${orderSQL}</span><span class="hljs-subst">${limitSQL}</span><span class="hljs-subst">${offsetSQL}</span>`</span>,</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// we need the aggregate function, if one's needed, to sit in an outer </span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// query, to keep ORDER and LIMIT working normally in the main query</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  query = mode !== <span class="hljs-title class_">SelectResultMode</span>.<span class="hljs-property">Many</span> ? rowsQuery :</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    sql&lt;<span class="hljs-variable constant_">SQL</span>, <span class="hljs-built_in">any</span>&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string">      SELECT coalesce(jsonb_agg(result), '[]') AS result </span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;"><span class="hljs-string">      FROM (<span class="hljs-subst">${rowsQuery}</span>) AS <span class="hljs-subst">${raw(<span class="hljs-string">`"sq_<span class="hljs-subst">${aliasedTable}</span>"`</span>)}</span>`</span>;</span></code></pre>
<h4 id="arrays">Arrays</h4>
<p>Items in an interpolated array are treated just the same as if they had been interpolated directly. This, again, can be useful for building queries programmatically.</p>
<p>To take the <a href="#select-selectone-and-selectexactlyone"><code>select</code> shortcut</a> as our example again, an interpolated array is used to generate <code>LATERAL JOIN</code> query elements from the <code>lateral</code> option, like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  lateralOpt = allOptions.<span class="hljs-property">lateral</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  lateralSQL = lateralOpt === <span class="hljs-literal">undefined</span> ? [] :</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(lateralOpt).<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">k</span> =&gt;</span> {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">const</span> subQ = lateralOpt[k];</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      subQ.<span class="hljs-property">parentTable</span> = aliasedTable;  <span class="hljs-comment">// enables `parent()` in subquery's Whereables</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">return</span> sql&lt;<span class="hljs-variable constant_">SQL</span>&gt;<span class="hljs-string">` LEFT JOIN LATERAL (<span class="hljs-subst">${subQ}</span>) AS <span class="hljs-subst">${raw(<span class="hljs-string">`"cj_<span class="hljs-subst">${k}</span>"`</span>)}</span> ON true`</span>;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    });</span></code></pre>
<p>The <code>lateralSQL</code> variable — a <code>SQLFragment[]</code> —&nbsp;is subsequently interpolated into the final query (some additional SQL using <code>jsonb_build_object()</code> is interpolated earlier in that query, to return the result of the lateral subquery alongside the main query columns).</p>
<p>Note that a useful idiom also seen here is the use of the empty array (<code>[]</code>) to conditionally interpolate nothing at all.</p>
<h4 id="rawvalue-string-dangerousrawstring"><code>raw(value: string): DangerousRawString</code></h4>
<p>The <code>raw</code> function returns <code>DangerousRawString</code> wrapper instances. This represents an escape hatch, enabling us to interpolate arbitrary strings into queries in contexts where the <code>param</code> wrapper is unsuitable (such as when we’re interpolating basic SQL syntax elements). <strong>If you pass user-controlled data to this function you will open yourself up to SQL injection attacks.</strong></p>
<h4 id="parentcolumnname-string-parentcolumn"><code>parent(columnName?: string): ParentColumn</code></h4>
<p>Within queries passed as subqueries to the <code>lateral</code> option of <code>select</code> and related queries, the <code>parent()</code> wrapper can be used to refer to a column of the table that’s the subject of the immediately containing query (the ‘parent’ table).</p>
<p>To refer to a column of the parent table by name, pass a <code>string</code> argument. If the column of the parent table has the same name as the column with which it’s being joined, no argument is required.</p>
<p>For usage details, see the <a href="#lateral-and-alias">documentation for the <code>lateral</code> option</a>.</p>
<h3 id="sqlfragment"><code>SQLFragment</code></h3>
<p><code>SQLFragment&lt;RunResult&gt;</code> class instances are what is returned by the <code>sql</code> tagged template function —&nbsp;you’re unlikely ever to contruct them directly with <code>new</code>. They take on the <code>RunResult</code> type variable from the <code>sql</code> template function that constructs them.</p>
<p>You can <a href="#sql-template-strings">interpolate them</a> into other <code>sql</code> tagged template strings, or call/access the following properties on them:</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/core.ts#L253">Source code »</a></div>
<h4 id="preparedname-string-this"><code>prepared(name: string): this</code></h4>
<p>The <code>prepared</code> function causes a <code>name</code> property to be added to the compiled SQL query object that’s passed to <code>pg</code>, and this <a href="https://node-postgres.com/features/queries#prepared-statements">instructs Postgres to treat it as a prepared statement</a>. You can specify a prepared statement name as the function’s argument, or let it default to <code>"_zapatos_prepared_N"</code> (where N is a sequence number). This name appears in the Postgres logs.</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/core.ts#L264">Source code »</a></div>
<h4 id="async-runqueryable-queryable-force--false-promiserunresult"><code>async run(queryable: Queryable, force = false): Promise&lt;RunResult&gt;</code></h4>
<p>The <code>run</code> function compiles, executes, and returns the transformed result of the query represented by this <code>SQLFragment</code>. The <code>awaited</code> return value is typed according to the <code>SQLFragment</code>’s <code>RunResult</code> type variable.</p>
<p>Taking that one step at a time:</p>
<ol>
<li>
<p>First, <a href="#compile-sqlquery">the <code>compile</code> function</a> is called, recursively compiling this <code>SQLFragment</code> and its interpolated values into a <code>{ text: '', values: [] }</code> query that can be passed straight to the <code>pg</code> module. If a <code>queryListener</code> function <a href="#run-time-configuration">has been configured</a>, it is called with the query as its argument now.</p>
</li>
<li>
<p>Next, the compiled SQL query is executed against the supplied <code>Queryable</code>, which is defined as a <code>pg.Pool</code> or <code>pg.ClientBase</code> (this definition also covers the <code>TxnClient</code> provided by the <a href="#transaction"><code>transaction</code> helper function</a>).</p>
</li>
<li>
<p>Finally, the result returned from <code>pg</code> is fed through this <code>SQLFragment</code>’s <a href="#runresulttransform-qr-pgqueryresult--any"><code>runResultTransform()</code></a> function, whose default implementation simply returns the <code>rows</code> property of the result. If a <code>resultListener</code> function <a href="#run-time-configuration">has been configured</a>, it is called with the transformed result as its argument now.</p>
</li>
</ol>
<p>Examples of the <code>run</code> function are scattered throughout this documentation.</p>
<p>The <code>force</code> parameter is relevant only if this <code>SQLFragment</code> has been marked as a <a href="https://en.wiktionary.org/wiki/no-op#Etymology_2">no-op</a>: at present, Zapatos does this automatically if you pass an empty array to <code>insert</code> or <code>upsert</code>. By default, the database will not be disturbed in such cases, but you can force a no-op query to actually be run against the database — perhaps for logging or triggering reasons — by setting <code>force</code> to <code>true</code>.</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/core.ts#L292">Source code »</a></div>
<h4 id="compile-sqlquery"><code>compile(): SQLQuery</code></h4>
<p>The <code>compile</code> function recursively transforms this <code>SQLFragment</code> and its interpolated values into a <code>SQLQuery</code> object (<code>{ text: string; values: any[]; }</code>) that can be passed straight to the <code>pg</code> module. It is called without arguments (the arguments it can take are for internal use).</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  authorId = <span class="hljs-number">12</span>,  <span class="hljs-comment">// from some untrusted source</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  query = db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT * FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> WHERE <span class="hljs-subst">${{authorId}}</span>`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  compiled = query.<span class="hljs-title function_">compile</span>();</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(compiled);</span></code></pre><div class="sqlstuff">
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  text: '\n    SELECT * FROM "books" WHERE ("authorId" = $1)',</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  values: [ 12 ]</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
</div>
<p>You may never need this function. Use it if and when you want to see the SQL that would be executed by the <code>run</code> function, without in fact executing it.</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/core.ts#L237">Source code »</a></div>
<h4 id="runresulttransform-qr-pgqueryresult--any"><code>runResultTransform: (qr: pg.QueryResult) =&gt; any</code></h4>
<p>When you call <code>run</code>, the function stored in this property is applied to the <code>QueryResult</code> object returned by <code>pg</code>, in order to produce the result that the <code>run</code> function ultimately returns.</p>
<p>By default, the <code>QueryResult</code>’s <code>rows</code> property (which is an array) is returned: that is, the default implementation is just <code>qr =&gt; qr.rows</code>. However, the <a href="#shortcut-functions-and-lateral-joins">shortcut functions</a> supply their own <code>runResultTransform</code> implementations in order to match their declared <code>RunResult</code> types.</p>
<p>Generally you will not need to call this function directly, but there may be cases where you want to assign a new function to replace the default implementation.</p>
<p>For example, imagine we wanted to create a function returning a query that, when run, returns the current database timestamp directly as a <code>Date</code>. We could do so like this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">function</span> <span class="hljs-title function_">dbNowQuery</span>(<span class="hljs-params"></span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> query = db.<span class="hljs-property">sql</span>&lt;<span class="hljs-built_in">never</span>, <span class="hljs-title class_">Date</span>&gt;<span class="hljs-string">`SELECT now()`</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  query.<span class="hljs-property">runResultTransform</span> = <span class="hljs-function"><span class="hljs-params">qr</span> =&gt;</span> qr.<span class="hljs-property">rows</span>[<span class="hljs-number">0</span>].<span class="hljs-property">now</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">return</span> query;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> dbNow = <span class="hljs-keyword">await</span> <span class="hljs-title function_">dbNowQuery</span>().<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// dbNow is a Date: the result you can toggle below has come via JSON.stringify</span></span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> now()</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-string">"2024-06-23T14:24:09.784Z"</span></span></code></pre>
</div>
<p>Note that the <code>RunResult</code> type variable on the <code>sql</code> template function (in this case, <code>Date</code>) must reflect the type of the <em>transformed</em> result, not what comes straight back from <code>pg</code> (which in this case is roughly <code>{ rows: [{ now: Date }] }</code>).</p>
<p>If a <code>SQLFragment</code> does not have <code>run</code> called on it directly —&nbsp;for example, if it is instead interpolated into another <code>SQLFragment</code>, or given as the value of the <code>lateral</code> option to the <code>select</code> shortcut — then the <code>runResultTransform</code> function is never applied.</p>
<h3 id="manual-joins-using-postgres-json-features">Manual joins using Postgres’ JSON features</h3>
<p>We can make use of Postgres’ excellent JSON support to achieve a variety of <code>JOIN</code> queries. That’s not unique to Zapatos, of course, but it may be helpful to consider a few example queries in this context.</p>
<p>Take this example, retrieving each book with its (single) author:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> bookAuthorSQL = s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span> | s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span> | <span class="hljs-string">"author"</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> bookAuthorSelectable = s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span> &amp; { <span class="hljs-attr">author</span>: s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span> };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query = db.<span class="hljs-property">sql</span>&lt;bookAuthorSQL, bookAuthorSelectable[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.*, to_jsonb(<span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.*) as <span class="hljs-subst">${<span class="hljs-string">"author"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> JOIN <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> </span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  ON <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"authorId"</span>}</span> = <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span>`</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> bookAuthors = <span class="hljs-keyword">await</span> query.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> "books".<span class="hljs-operator">*</span>, to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">as</span> "author"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">JOIN</span> "authors" <span class="hljs-keyword">ON</span> "books"."authorId" <span class="hljs-operator">=</span> "authors"."id"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.603Z"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.605Z"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Holes"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T14:22:52.605Z"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Of course, we might also want the converse query, retrieving each author with their (many) books. This is also easy enough to arrange:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> authorBooksSQL = s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span> | s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> authorBooksSelectable = s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span> &amp; { <span class="hljs-attr">books</span>: s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[] };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query = db.<span class="hljs-property">sql</span>&lt;authorBooksSQL, authorBooksSelectable[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.*, jsonb_agg(<span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.*) AS <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  FROM <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> JOIN <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span> </span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  ON <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span> = <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"authorId"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  GROUP BY <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span>`</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authorBooks = <span class="hljs-keyword">await</span> query.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> "authors".<span class="hljs-operator">*</span>, jsonb_agg("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">JOIN</span> "books" <span class="hljs-keyword">ON</span> "authors"."id" <span class="hljs-operator">=</span> "books"."authorId"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> "authors"."id"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Holes"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605559+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Note that if you want to include authors with no books, you need a <code>LEFT JOIN</code> in this query, and then you’ll also want to fix the annoying <a href="https://stackoverflow.com/questions/24155190/postgresql-left-join-json-agg-ignore-remove-null"><code>[null]</code> array results <code>jsonb_agg</code> will return for those authors</a>.</p>
<p>Rather than do it that way, though, we can achieve the same result using a <a href="https://medium.com/kkempin/postgresqls-lateral-join-bfd6bd0199df"><code>LATERAL JOIN</code></a> instead:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> authorBooksSQL = s.<span class="hljs-property">authors</span>.<span class="hljs-property">SQL</span> | s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">type</span> authorBooksSelectable = s.<span class="hljs-property">authors</span>.<span class="hljs-property">Selectable</span> &amp; { <span class="hljs-attr">books</span>: s.<span class="hljs-property">books</span>.<span class="hljs-property">Selectable</span>[] };</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query = db.<span class="hljs-property">sql</span>&lt;authorBooksSQL, authorBooksSelectable[]&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  SELECT <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.*, bq.* </span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  FROM <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span> LEFT JOIN LATERAL (</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT coalesce(json_agg(<span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.*), '[]') AS <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    FROM <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    WHERE <span class="hljs-subst">${<span class="hljs-string">"books"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"authorId"</span>}</span> = <span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  ) bq ON true`</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authorBooks = <span class="hljs-keyword">await</span> query.<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> "authors".<span class="hljs-operator">*</span>, bq.<span class="hljs-operator">*</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(json_agg("books".<span class="hljs-operator">*</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> "books"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> "books"."authorId" <span class="hljs-operator">=</span> "authors"."id") bq <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Holes"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605559+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Lateral joins of this sort are very flexible, and can be nested multiple levels deep — but can quickly become quite hairy in that case. The <a href="#select-selectone-and-selectexactlyone"><code>select</code> shortcut function</a> and its <a href="#lateral-and-alias"><code>lateral</code> option</a> can make this much less painful.</p>
<h3 id="shortcut-functions-and-lateral-joins">Shortcut functions and lateral joins</h3>
<p>A key contribution of Zapatos is a set of simple shortcut functions that make everyday <a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> queries extremely easy to work with. Furthermore, the <code>select</code> shortcut can be nested in order to generate <a href="https://www.postgresql.org/docs/12/queries-table-expressions.html#id-1.5.6.6.5.10.2">LATERAL JOIN</a> queries, resulting in arbitrarily complex nested JSON structures with inputs and outputs that are still fully and automatically typed.</p>
<p>The shortcut functions make heavy use of Postgres’ JSON support, and their return values are thus <a href="#jsonselectable"><code>JSONSelectable</code></a>s rather than the plain <code>Selectable</code>s you’d get back from a manual query.</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L93">Source code »</a></div>
<h4 id="insert"><code>insert</code></h4>
<p>The <code>insert</code> shortcut inserts one or more rows in a table, and returns them with any <code>DEFAULT</code> or generated values filled in. It takes a <code>Table</code> name and the corresponding <code>Insertable</code> or <code>Insertable[]</code>, and returns the corresponding <code>JSONSelectable</code> or <code>JSONSelectable[]</code> (subject to the options described below).</p>
<p>The optional <code>options</code> argument has two keys.</p>
<ul>
<li>
<p><code>returning</code> takes an array of column names, and narrows down the returned values accordingly. This may be useful if you are inserting large objects which you prefer don’t take an inefficient return trip over the wire and through the JSON parser.</p>
</li>
<li>
<p><code>extras</code> takes a map of string keys to column names and/or <code>sql</code> template strings (i.e. <code>SQLFragments</code>), allowing you to alias certain columns and/or compute and return other quantities alongside them. The <code>RunResult</code> type variable matters in the case of template strings, as it is passed through to the result type.</p>
</li>
</ul>
<p>(Note that type inference can only do the right thing with <code>returning</code> and <code>extras</code> when <code>strictNullChecks</code> are enabled).</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// insert one</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  steve = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'authors'</span>, { </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">name</span>: <span class="hljs-string">'Steven Hawking'</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">isLiving</span>: <span class="hljs-literal">false</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// insert many</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [time, me] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'books'</span>, [{ </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">authorId</span>: steve.<span class="hljs-property">id</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">title</span>: <span class="hljs-string">'A Brief History of Time'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">createdAt</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`now()`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }, { </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">authorId</span>: steve.<span class="hljs-property">id</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">title</span>: <span class="hljs-string">'My Brief History'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">createdAt</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`now()`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }]).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  tags = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'tags'</span>, [</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">bookId</span>: time.<span class="hljs-property">id</span>, <span class="hljs-attr">tag</span>: <span class="hljs-string">'physics'</span> },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">bookId</span>: me.<span class="hljs-property">id</span>, <span class="hljs-attr">tag</span>: <span class="hljs-string">'physicist'</span> },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">bookId</span>: me.<span class="hljs-property">id</span>, <span class="hljs-attr">tag</span>: <span class="hljs-string">'autobiography'</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// insert with custom return values</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  nutshell = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'books'</span>, { </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">authorId</span>: steve.<span class="hljs-property">id</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">title</span>: <span class="hljs-string">'The Universe in a Nutshell'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">createdAt</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`now()`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }, {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">returning</span>: [<span class="hljs-string">'id'</span>],</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">extras</span>: { </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">aliasedTitle</span>: <span class="hljs-string">"title"</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">upperTitle</span>: db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;<span class="hljs-string">`upper(<span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span>)`</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors" ("isLiving", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Steven Hawking"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Steven Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "books" ("authorId", "createdAt", "title")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, now(), $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, now(), $<span class="hljs-number">4</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">5</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "tags" ("bookId", "tag")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>), ($<span class="hljs-number">5</span>, $<span class="hljs-number">6</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("tags".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"physics"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"physicist"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"autobiography"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physics"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physicist"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"autobiography"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "books" ("authorId", "createdAt", "title")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, now(), $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING jsonb_build_object($<span class="hljs-number">3</span>::text, "id") <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">4</span>::text, "title", $<span class="hljs-number">5</span>::text, <span class="hljs-built_in">upper</span>("title")) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">5</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"id"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"aliasedTitle"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"upperTitle"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"upperTitle"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"THE UNIVERSE IN A NUTSHELL"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"aliasedTitle"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>You’ll note that <code>Insertable</code>s can take <code>SQLFragment</code> values (from the <code>sql</code> tagged template function) as well as direct values (strings, numbers, and so on).</p>
<p>Postgres can accept up to 65,536 parameters per query (since <a href="https://stackoverflow.com/questions/6581573/what-are-the-max-number-of-allowable-parameters-per-database-provider-type/49379324#49379324">an Int16 is used</a> to convey the number of parameters in the <em>Bind</em> message of the <a href="https://www.postgresql.org/docs/current/protocol-message-formats.html">wire protocol</a>). If there’s a risk that a multiple-row <code>INSERT</code> could have more inserted values than that, you’ll need a mechanism to batch them up into separate calls.</p>
<p>If you provide an empty array to <code>insert</code>, this is identified as a no-op, and the database will not actually be queried unless you set the <code>force</code> option on <code>run</code> to true.</p>
<pre class="language-typescript showempty runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">"authors"</span>, []).<span class="hljs-title function_">run</span>(pool);  <span class="hljs-comment">// never reaches DB</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">"authors"</span>, []).<span class="hljs-title function_">run</span>(pool, <span class="hljs-literal">true</span>);  <span class="hljs-comment">// does reach DB, for same result</span></span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">/* marked no-op: won't hit DB unless forced -&gt; */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">null</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> <span class="hljs-literal">false</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">/* marked no-op: won't hit DB unless forced -&gt; */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">null</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> <span class="hljs-literal">false</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L284">Source code »</a></div>
<h4 id="update"><code>update</code></h4>
<p>The <code>update</code> shortcut updates rows in the database. It takes a <code>Table</code> name and a corresponding <code>Updatable</code> and <code>Whereable</code> <strong>in that order, matching their order in the raw SQL query</strong>.</p>
<p>It returns a <code>JSONSelectable[]</code>, listing every column of every row affected (or a subset or superset of those columns, if you use the <code>returning</code> and/or <code>extras</code> options, which work just as described above for <code>insert</code>).</p>
<p>For example, when we discover with that we’ve mis-spelled a famous physicist’s name, we can do this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">update</span>(<span class="hljs-string">'authors'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Stephen Hawking'</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Steven Hawking'</span> }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("name") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("name" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Steven Hawking"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Like <code>Insertable</code> values, <code>Updatable</code> values can also be <code>SQLFragment</code>s. For instance, take a table such as the following:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "emailAuthentication" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "email" citext <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "consecutiveFailedLogins" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-number">0</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "lastFailedLogin" TIMESTAMPTZ );</span></code></pre>
<p>To atomically increment the <code>consecutiveFailedLogins</code> value, we can do something like this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">update</span>(<span class="hljs-string">"emailAuthentication"</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">consecutiveFailedLogins</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> + 1`</span>,  </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// or equivalently: consecutiveFailedLogins: dc.add(1),</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lastFailedLogin</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`now()`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// or equivalently: lastFailedLogin: dc.now,</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}, { <span class="hljs-attr">email</span>: <span class="hljs-string">'me@privacy.net'</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "emailAuthentication"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("consecutiveFailedLogins", "lastFailedLogin") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("consecutiveFailedLogins" <span class="hljs-operator">+</span> <span class="hljs-number">1</span>, now())</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("email" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("emailAuthentication".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"me@privacy.net"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"email"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"me@privacy.net"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"lastFailedLogin"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:11.210042+01:00"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"consecutiveFailedLogins"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L147">Source code »</a></div>
<h4 id="upsert"><code>upsert</code></h4>
<p>The <code>upsert</code> shortcut issues an <a href="https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT"><code>INSERT ... ON CONFLICT ...</code></a> query. Like <code>insert</code>, it takes a <code>Table</code> name and a corresponding <code>Insertable</code> or <code>Insertable[]</code>.</p>
<p>It then takes, in addition, a column name (or an array thereof) or an appropriate unique index as the conflict target: the ‘arbiter index(es)’ on which a conflict is to be detected.</p>
<p>It returns an <code>UpsertReturnable</code> or <code>UpsertReturnable[]</code>. An <code>UpsertReturnable</code> is the same as a <code>JSONSelectable</code> except that it includes one additional property, <code>$action</code>, taking the string <code>'INSERT'</code> or <code>'UPDATE'</code> so as to indicate which eventuality occurred for each row.</p>
<p>Let’s say we have a table of app subscription transactions:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "appleTransactions" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "environment" "appleEnvironment" <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span>  <span class="hljs-comment">-- enum: 'PROD' or 'Sandbox'</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "originalTransactionId" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "accountId" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">REFERENCES</span> "accounts"("id") <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "latestReceiptData" TEXT );</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> "appleTransactions" <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">CONSTRAINT</span> "appleTransactionsPrimaryKey" </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">PRIMARY</span> KEY ("environment", "originalTransactionId");</span></code></pre>
<p>When we receive a purchase receipt, we need to either store a new record or update an existing record for each distinct (<code>environment</code>, <code>originalTransactionId</code>) it contains.</p>
<p>We can <code>map</code> the transaction data in the receipt into an <code>appleTransactions.Insertable[]</code>, and do what’s needed with a single <code>upsert</code> call. In this example, though, we hard-code the <code>Insertable[]</code> for ease of exposition:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">newTransactions</span>: s.<span class="hljs-property">appleTransactions</span>.<span class="hljs-property">Insertable</span>[] = [{</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">environment</span>: <span class="hljs-string">'PROD'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">originalTransactionId</span>: <span class="hljs-string">'123456'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">accountId</span>: <span class="hljs-number">123</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">latestReceiptData</span>: <span class="hljs-string">'TWFuIGlzIGRpc3Rp'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }, {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">environment</span>: <span class="hljs-string">'PROD'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">originalTransactionId</span>: <span class="hljs-string">'234567'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">accountId</span>: <span class="hljs-number">234</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">latestReceiptData</span>: <span class="hljs-string">'bmd1aXNoZWQsIG5v'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }],</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  result = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">upsert</span>(<span class="hljs-string">'appleTransactions'</span>, newTransactions, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    [<span class="hljs-string">'environment'</span>, <span class="hljs-string">'originalTransactionId'</span>]).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "appleTransactions" ("accountId", "environment", "latestReceiptData", "originalTransactionId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>, $<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>), ($<span class="hljs-number">5</span>, $<span class="hljs-number">6</span>, $<span class="hljs-number">7</span>, $<span class="hljs-number">8</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT ("environment", "originalTransactionId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ("environment", "originalTransactionId", "accountId", "latestReceiptData") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> (EXCLUDED. "environment", EXCLUDED. "originalTransactionId", EXCLUDED. "accountId", EXCLUDED. "latestReceiptData")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  RETURNING to_jsonb ("appleTransactions".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">123</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"TWFuIGlzIGRpc3Rp"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"123456"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">234</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"bmd1aXNoZWQsIG5v"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"234567"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"UPDATE"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"accountId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">123</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"environment"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"latestReceiptData"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"TWFuIGlzIGRpc3Rp"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"originalTransactionId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"123456"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"INSERT"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"accountId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">234</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"environment"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"latestReceiptData"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"bmd1aXNoZWQsIG5v"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"originalTransactionId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"234567"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>And it’s wholly equivalent here to use the unique index name instead of the column names for the conflict target, by using the <code>constraint</code> wrapper function:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">anotherNewTransaction</span>: s.<span class="hljs-property">appleTransactions</span>.<span class="hljs-property">Insertable</span> = {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">environment</span>: <span class="hljs-string">'PROD'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">originalTransactionId</span>: <span class="hljs-string">'345678'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">accountId</span>: <span class="hljs-number">345</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">latestReceiptData</span>: <span class="hljs-string">'lALvEleO4Ehwk3T5'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  result = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">upsert</span>(<span class="hljs-string">'appleTransactions'</span>, anotherNewTransaction, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    db.<span class="hljs-title function_">constraint</span>(<span class="hljs-string">'appleTransactionsPrimaryKey'</span>)).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "appleTransactions" ("accountId", "environment", "latestReceiptData", "originalTransactionId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>, $<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT <span class="hljs-keyword">ON</span> <span class="hljs-keyword">CONSTRAINT</span> "appleTransactionsPrimaryKey"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ("environment", "originalTransactionId", "accountId", "latestReceiptData") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> (EXCLUDED. "environment", EXCLUDED. "originalTransactionId", EXCLUDED. "accountId", EXCLUDED. "latestReceiptData")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  RETURNING to_jsonb ("appleTransactions".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">345</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"lALvEleO4Ehwk3T5"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"345678"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"INSERT"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"accountId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">345</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"environment"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"PROD"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"latestReceiptData"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"lALvEleO4Ehwk3T5"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"originalTransactionId"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"345678"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>The same as for <code>insert</code>, an empty array provided to <code>upsert</code> is identified as a no-op, and the database will not actually be queried unless you set the <code>force</code> option on <code>run</code> to true.</p>
<h5 id="upsert-options"><code>upsert</code> options</h5>
<p>The optional fourth argument to <code>upsert</code> is an <code>options</code> object. The available options are <code>returning</code> and <code>extras</code> (see the documentation for <code>insert</code> for details) plus <code>updateColumns</code>, <code>noNullUpdateColumns</code>, <code>updateValues</code> and <code>reportAction</code>.</p>
<ul>
<li>
<p>The <code>updateColumns</code> option allows us to specify a subset of columns (as either one name or an array of names) that are to be updated on conflict. For example, you might want to include all columns except <code>createdAt</code> in this list.</p>
</li>
<li>
<p>The <code>noNullUpdateColumns</code> option takes a column name or array of column names which are not to be overwritten with <code>NULL</code> in the case that the <code>UPDATE</code> branch is taken. It can also take the special value <code>db.all</code> to indicate that no column should ever be overwritten with <code>NULL</code>.</p>
</li>
<li>
<p>The <code>updateValues</code> option allows us to specify alternative column values to be used in the <code>UPDATE</code> query branch: <a href="#updatevalues">see below</a>.</p>
</li>
<li>
<p>The <code>reportAction: 'suppress'</code> option causes the <code>$action</code> result key to be omitted, so the query returns plain <code>JSONSelectable</code> instead of <code>UpsertReturnable</code> results.</p>
</li>
</ul>
<h5 id="insert--on-conflict--do-nothing"><code>INSERT ... ON CONFLICT ... DO NOTHING</code></h5>
<p>A special case arises if you pass the empty array <code>[]</code> to the <code>updateColumns</code> option of <code>upsert</code>.</p>
<p>Since no columns are then to be updated in case of a conflict, an <code>ON CONFLICT ... DO NOTHING</code> query is generated instead of an <code>ON CONFLICT ... DO UPDATE ...</code> query. For better self-documenting code, an alias for the empty array is provided for this case: <code>doNothing</code>.</p>
<p>Since nothing is returned by Postgres for any <code>DO NOTHING</code> cases, a query with <code>updateColumns: []</code> or <code>updateColumns: db.doNothing</code> may return fewer rows than were passed in. If you pass in an array, you could get back an empty array if all rows conflict with existing rows. If you pass in values of a single row, you’ll get back <code>undefined</code> if a conflict occurs (and the return type will automatically reflect this).</p>
<p>For example:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "usedVoucherCodes" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "code" text <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "redeemedAt" timestamptz <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">DEFAULT</span> now()</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span></code></pre>
<pre class="language-typescript shownull runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// unused code: returns the inserted row</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> a = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">upsert</span>(<span class="hljs-string">'usedVoucherCodes'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">code</span>: <span class="hljs-string">'XYE953ZVU767'</span> }, <span class="hljs-string">'code'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">updateColumns</span>: db.<span class="hljs-property">doNothing</span> }).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// same code, already used: returns undefined</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> b = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">upsert</span>(<span class="hljs-string">'usedVoucherCodes'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">code</span>: <span class="hljs-string">'XYE953ZVU767'</span> }, <span class="hljs-string">'code'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">updateColumns</span>: db.<span class="hljs-property">doNothing</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "usedVoucherCodes" ("code")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT ("code")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO NOTHING</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("usedVoucherCodes".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"XYE953ZVU767"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"code"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"XYE953ZVU767"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"INSERT"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"redeemedAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:11.733317+01:00"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "usedVoucherCodes" ("code")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT ("code")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO NOTHING</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("usedVoucherCodes".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"XYE953ZVU767"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">undefined</span></code></pre>
</div>
<h5 id="updatevalues"><code>updateValues</code></h5>
<p>You can use the <code>updateValues</code> option to specify alternative column values to be used in the <code>UPDATE</code> branch of the query. Only one set of values can be provided: these will be used for any and all rows that get updated.</p>
<p>This may be useful, for example, when keeping a count, using a table such as this:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "nameCounts" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "name" text <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "count" <span class="hljs-type">integer</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span></code></pre>
<p>In the following query, we insert a new value with a count of 1 if a name doesn’t already exist in the table. If a name does exist, we increment the existing count instead:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">upsert</span>(<span class="hljs-string">'nameCounts'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Alice'</span>, <span class="hljs-attr">count</span>: <span class="hljs-number">1</span> }, <span class="hljs-string">'name'</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">updateValues</span>: { <span class="hljs-attr">count</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-string">"nameCounts"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"count"</span>}</span> + 1`</span> } }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "nameCounts" ("count", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT ("name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ("name", "count") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> (EXCLUDED. "name", "nameCounts"."count" <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  RETURNING to_jsonb ("nameCounts".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"count"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"INSERT"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "nameCounts" ("count", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ON</span> CONFLICT ("name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  DO <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">SET</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ("name", "count") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> (EXCLUDED. "name", "nameCounts"."count" <span class="hljs-operator">+</span> <span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  RETURNING to_jsonb ("nameCounts".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object(<span class="hljs-string">'$action'</span>, <span class="hljs-keyword">CASE</span> xmax</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHEN</span> <span class="hljs-number">0</span> <span class="hljs-keyword">THEN</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'INSERT'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">ELSE</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-string">'UPDATE'</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">END</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"count"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"$action"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"UPDATE"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L321">Source code »</a></div>
<h4 id="deletes"><code>deletes</code></h4>
<p>The <code>deletes</code> shortcut, unsurprisingly, deletes rows from a table (<code>delete</code>, unfortunately, is a JavaScript reserved word). It takes the table name and an appropriate <code>Whereable</code> or <code>SQLFragment</code>, and by default returns the deleted rows as a <code>JSONSelectable</code>.</p>
<p>Again, you can narrow or broaden what’s returned with the <code>returning</code> and <code>extras</code> options, as documented above for <code>insert</code>.</p>
<p>For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">deletes</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">title</span>: <span class="hljs-string">'Holes'</span> }, { <span class="hljs-attr">returning</span>: [<span class="hljs-string">'id'</span>] }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("title" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING jsonb_build_object($<span class="hljs-number">2</span>::text, "id") <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Holes"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"id"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1004</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L352">Source code »</a></div>
<h4 id="truncate"><code>truncate</code></h4>
<p>The <code>truncate</code> shortcut truncates one or more tables. It takes a <code>Table</code> name or a <code>Table[]</code> name array, and (optionally) the options <code>'CONTINUE IDENTITY'</code>/<code>'RESTART IDENTITY'</code> and/or <code>'RESTRICT'</code>/<code>'CASCADE'</code>.</p>
<p>For instance:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">truncate</span>(<span class="hljs-string">'bankAccounts'</span>).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">TRUNCATE</span> "bankAccounts"</span></code></pre>
</div>
<p>One context in which this may be useful is in emptying a testing database at the start of each test run.</p>
<p>First, we list all our tables. Zapatos provides some <a href="#utility-types">utility types</a> such as <code>AllBaseTables</code>, to help ensure that we don’t forget any:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-attr">allTables</span>: s.<span class="hljs-property">AllBaseTables</span> = [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'appleTransactions'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'arrays'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'authors'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'bigints'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'books'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'doctors'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'emailAuthentication'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'employees'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'nameCounts'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'numerics'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'photos'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'shifts'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'stores'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'subjectPhotos'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'subjects'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'tags'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'usedVoucherCodes'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-string">'users'</span>,</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">];</span></code></pre>
<p>We can then empty the database like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// *** DON'T DO THIS IN PRODUCTION! ***</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">truncate</span>(allTables, <span class="hljs-string">'CASCADE'</span>).<span class="hljs-title function_">run</span>(pool);</span></code></pre>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L384">Source code »</a></div>
<h4 id="select-selectone-and-selectexactlyone"><code>select</code>, <code>selectOne</code>, and <code>selectExactlyOne</code></h4>
<p>The <code>select</code> shortcut function, in its basic form, takes a <code>Table</code> name and some <code>WHERE</code> conditions, and returns a <code>SQLFragment&lt;JSONSelectable[]&gt;</code>. Those <code>WHERE</code> conditions can be the symbol <code>all</code> (meaning: no conditions), a <code>SQLFragment</code> from a <code>sql</code> template string, or the appropriate <code>Whereable</code> for the target table (recall that <a href="#whereable">a <code>Whereable</code> can itself contain <code>SQLFragment</code> values</a>).</p>
<p><code>selectOne</code> does the same, except that it gives us a <code>SQLFragment&lt;JSONSelectable | undefined&gt;</code>, promising <em>only a single object</em> (or <code>undefined</code>) when run.</p>
<p><code>selectExactlyOne</code> function does the same as <code>selectOne</code>, except that it eliminates the <code>undefined</code> case (to give: <code>SQLFragment&lt;JSONSelectable&gt;</code>). Instead, it will throw an error (with a helpful <code>query</code> property) if it doesn’t find a row.</p>
<p>In use, they look like this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// select, no WHERE clause</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> allBooks = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// select, Whereable</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authorBooks = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">authorId</span>: <span class="hljs-number">1000</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">WHERE</span> ("authorId" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1000</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// selectOne (since authors.id is a primary key), Whereable</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> oneAuthor = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">selectOne</span>(<span class="hljs-string">'authors'</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">1000</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT $<span class="hljs-number">2</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1000</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// selectExactlyOne, Whereable</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// (for a more useful example, see the section on `lateral`, below)</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> exactlyOneAuthor = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">selectExactlyOne</span>(<span class="hljs-string">'authors'</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">999</span> }).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// ... do something with this author ...</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">} <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">if</span> (err <span class="hljs-keyword">instanceof</span> db.<span class="hljs-property">NotExactlyOneError</span>) <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`<span class="hljs-subst">${err.name}</span>: <span class="hljs-subst">${err.message}</span>`</span>);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">else</span> <span class="hljs-keyword">throw</span> err;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT $<span class="hljs-number">2</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">999</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">NotExactlyOneError: One result expected but none returned (hint: check `.query.compile()` on this Error)</span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// select, Whereable with embedded SQLFragment</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> recentAuthorBooks = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">authorId</span>: <span class="hljs-number">1001</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">createdAt</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> &gt; now() - INTERVAL '7 days'`</span>,</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">WHERE</span> ("authorId" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">AND</span> ("createdAt" <span class="hljs-operator">&gt;</span> now() <span class="hljs-operator">-</span> <span class="hljs-type">INTERVAL</span> <span class="hljs-string">'7 days'</span>))) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1001</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// select, Whereables with conditions helpers</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> alsoRecentAuthorBooks = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">authorId</span>: <span class="hljs-number">1001</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">createdAt</span>: dc.<span class="hljs-title function_">after</span>(dc.<span class="hljs-title function_">fromNow</span>(-<span class="hljs-number">7</span>, <span class="hljs-string">'days'</span>)),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">WHERE</span> ("authorId" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">AND</span> ("createdAt" <span class="hljs-operator">&gt;</span> now() <span class="hljs-operator">+</span> $<span class="hljs-number">2</span>))) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1001</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"-7 days"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// select, SQLFragment with embedded Whereables</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> anOddSelectionOfBooksToDemonstrateAnOrCondition = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>&gt;<span class="hljs-string">`<span class="hljs-subst">${{ id: <span class="hljs-number">1</span> }}</span> OR <span class="hljs-subst">${{ authorId: <span class="hljs-number">2</span> }}</span>`</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">OR</span> ("authorId" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Similar to our earlier shortcut examples, once I’ve typed in <code>'books'</code> or <code>'authors'</code> as the first argument to the function, TypeScript and VS Code know both how to type-check and auto-complete both the <code>WHERE</code> argument and the type that will returned by <code>run</code>.</p>
<p>The <code>select</code> and <code>selectOne</code> shortcuts can also take an <code>options</code> object as their third argument, which has a large set of potential keys: <code>columns</code>, <code>order</code>, <code>limit</code>, <code>offset</code>, <code>lateral</code>, <code>alias</code>, <code>extras</code>, <code>groupBy</code>, <code>having</code>, <code>distinct</code> and <code>lock</code>.</p>
<h5 id="columns"><code>columns</code></h5>
<p>The <code>columns</code> key specifies that we want to return only a subset of columns, perhaps for reasons of efficiency. It takes an array of <code>Column</code> names for the appropriate table, and works in just the same way as the <code>returning</code> option on the other query types. For example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> bookTitles = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">columns</span>: [<span class="hljs-string">'title'</span>] }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "title") <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"title"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The return type is of course appropriately narrowed to the requested columns only, so VS Code will complain if we now try to access <code>bookTitles[0].authorId</code>, for example. (Note: this works only when <code>strictNullChecks</code> are in operation).</p>
<p>The <code>columns</code> option does not enable column aliasing —&nbsp;i.e. you can’t use it to do <code>SELECT "column" AS "aliasedColumn"</code> or its equivalent — but column aliasing <em>is</em> easily achieved using the <code>extras</code> option instead.</p>
<h5 id="order-limit-and-offset"><code>order</code>, <code>limit</code> and <code>offset</code></h5>
<p>The <code>limit</code> and <code>offset</code> options each take a number and pass it directly through to SQL <code>LIMIT</code> and <code>OFFSET</code> clauses. The <code>order</code> option takes a single <code>OrderSpecForTable</code> or an <code>OrderSpecForTable[]</code> array, which has this shape:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">OrderSpecForTable</span>&lt;T <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Table</span>&gt; {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">by</span>: <span class="hljs-title class_">SQLForTable</span>&lt;T&gt;;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">direction</span>: <span class="hljs-string">'ASC'</span> | <span class="hljs-string">'DESC'</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  nulls?: <span class="hljs-string">'FIRST'</span> | <span class="hljs-string">'LAST'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<p>Putting them together gives us queries like this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [lastButOneBook] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">order</span>: { <span class="hljs-attr">by</span>: <span class="hljs-string">'createdAt'</span>, <span class="hljs-attr">direction</span>: <span class="hljs-string">'DESC'</span> }, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">limit</span>: <span class="hljs-number">1</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">offset</span>: <span class="hljs-number">1</span>,</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> "createdAt" <span class="hljs-keyword">DESC</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  LIMIT $<span class="hljs-number">1</span> <span class="hljs-keyword">OFFSET</span> $<span class="hljs-number">2</span>) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>I used destructuring assignment here (<code>const [lastButOneBook] = /* ... */;</code>) to account for the fact that I know this query is only going to return one response. Unfortunately, destructuring is just syntactic sugar for indexing, and indexing in TypeScript <a href="https://github.com/Microsoft/TypeScript/issues/13778">doesn’t reflect that the result may be undefined</a> unless you have <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#no-unchecked-indexed-access"><code>--noUncheckedIndexedAccess</code></a> turned on. That means that <code>lastButOneBook</code> is now typed as a <code>JSONSelectable</code>, but it could actually be <code>undefined</code>, and that could lead to errors down the line.</p>
<p>To fix this, we can use the <code>selectOne</code> function instead, which turns the example above into the following:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> lastButOneBook = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">selectOne</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">order</span>: [{ <span class="hljs-attr">by</span>: <span class="hljs-string">'createdAt'</span>, <span class="hljs-attr">direction</span>: <span class="hljs-string">'DESC'</span> }], </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">offset</span>: <span class="hljs-number">1</span> </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> "createdAt" <span class="hljs-keyword">DESC</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT $<span class="hljs-number">1</span> <span class="hljs-keyword">OFFSET</span> $<span class="hljs-number">2</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>The <code>{ limit: 1 }</code> option is now applied automatically. And the return type following <code>await</code> needs no destructuring and is now, correctly, <code>JSONSelectable | undefined</code>.</p>
<h5 id="lateral-and-alias"><code>lateral</code> and <code>alias</code></h5>
<p>Earlier we put together <a href="#manual-joins-using-postgres-json-features">some big <code>LATERAL</code> joins of authors and books</a>. This was a powerful and satisfying application of Postgres’ JSON support … but also a bit of an eyesore, heavy on both punctuation and manually constructed and applied types.</p>
<p>We can improve on this. Since <code>SQLFragments</code> are already designed to contain other <code>SQLFragments</code>, it’s a pretty small leap to enable <code>select</code> calls to be nested inside other <code>select</code> calls in order to significantly simplify this kind of <code>LATERAL</code> join query.</p>
<p>We achieve this with an additional <code>options</code> key, <code>lateral</code>. This <code>lateral</code> key takes either a single nested query shortcut, or an object that maps one or more property names to query shortcuts.</p>
<h6 id="lateral-property-maps"><code>lateral</code> property maps</h6>
<p>Let’s deal with the latter case —&nbsp;the map of property names to query shortcuts — first. It allows us to write an even bigger join (of books, each with their author and tags) like so:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> booksAuthorTags = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">author</span>: db.<span class="hljs-title function_">selectExactlyOne</span>(<span class="hljs-string">'authors'</span>, { <span class="hljs-attr">id</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'authorId'</span>) }),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">tags</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'tags'</span>, { <span class="hljs-attr">bookId</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'id'</span>) }),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "lateral_author".<span class="hljs-keyword">result</span>, $<span class="hljs-number">2</span>::text, "lateral_tags".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> "books"."authorId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  LIMIT $<span class="hljs-number">3</span>) <span class="hljs-keyword">AS</span> "lateral_author" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">SELECT</span> to_jsonb ("tags".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">FROM</span> "tags"</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHERE</span> ("bookId" <span class="hljs-operator">=</span> "books"."id")) <span class="hljs-keyword">AS</span> "sq_tags") <span class="hljs-keyword">AS</span> "lateral_tags" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"author"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"tags"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"3/3"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"mystery"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physics"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physicist"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"autobiography"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"bookId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"author"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The result here is a <code>books.JSONSelectable</code>, augmented with both an <code>author</code> property (containing an <code>authors.JSONSelectable</code>) and a <code>tags</code> property (containing a <code>tags.JSONSelectable[]</code> array).</p>
<p>Note that we use <code>selectExactlyOne</code> in the nested author query because a book’s <code>authorId</code> is defined as <code>NOT NULL REFERENCES "authors"("id")</code>, and we can therefore be 100% certain that we’ll get back a row here.</p>
<p>We could of course turn this around, nesting more deeply to retrieve authors, each with their books, each with their tags:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authorsBooksTags = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'authors'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">books</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">authorId</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'id'</span>) }, {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">tags</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'tags'</span>, { <span class="hljs-attr">bookId</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'id'</span>) }, { <span class="hljs-attr">columns</span>: [<span class="hljs-string">'tag'</span>] })</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      }</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    })</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "lateral_books".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">SELECT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">2</span>::text, "lateral_tags".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">3</span>::text, "tag") <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-keyword">FROM</span> "tags"</span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-keyword">WHERE</span> ("bookId" <span class="hljs-operator">=</span> "books"."id")) <span class="hljs-keyword">AS</span> "sq_tags") <span class="hljs-keyword">AS</span> "lateral_tags" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHERE</span> ("authorId" <span class="hljs-operator">=</span> "authors"."id")) <span class="hljs-keyword">AS</span> "sq_books") <span class="hljs-keyword">AS</span> "lateral_books" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">AS</span> "sq_authors"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"books"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"tags"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"tag"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1/3"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2/3"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"His Dark Materials"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"3/3"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"mystery"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"books"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physics"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"physicist"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 16ch; text-indent: -16ch;">            <span class="hljs-attr">"tag"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"autobiography"</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"tags"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>You’ll note the use of the <code>parent</code> function to refer to a join column in the table of the containing query. This is a simple convenience: in the join of books to authors above, we could just as well formulate the <code>Whereable</code> as:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{ <span class="hljs-attr">authorId</span>: sql<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-string">"authors"</span>}</span>.<span class="hljs-subst">${<span class="hljs-string">"id"</span>}</span>`</span> }</span></code></pre>
<p>We can also nest <a href="#count-avg-sum-min-and-max">aggregate calls such as <code>count</code></a>. And we can join a table to itself, though in that case we <em>must</em> remember to use the <code>alias</code> option to define an alternative table name, resolving ambiguity for Postgres.</p>
<p>Take this new, self-referencing table:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "employees"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "name" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "managerId" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">REFERENCES</span> "employees"("id") );</span></code></pre>
<p>Add some employees:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  anna = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'employees'</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Anna'</span> }).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [beth, charlie] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'employees'</span>, [</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Beth'</span>, <span class="hljs-attr">managerId</span>: anna.<span class="hljs-property">id</span> },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Charlie'</span>, <span class="hljs-attr">managerId</span>: anna.<span class="hljs-property">id</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  dougal = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'employees'</span>, </span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Dougal'</span>, <span class="hljs-attr">managerId</span>: beth.<span class="hljs-property">id</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "employees" ("name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("employees".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Anna"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Anna"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"managerId"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "employees" ("managerId", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("employees".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Beth"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Charlie"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Beth"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"managerId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Charlie"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"managerId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "employees" ("managerId", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("employees".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Dougal"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Dougal"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"managerId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>Then query for a summary (joining the table to itself twice, with appropriate aliasing):</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> people = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'employees'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">columns</span>: [<span class="hljs-string">'name'</span>], </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">lineManager</span>: db.<span class="hljs-title function_">selectOne</span>(<span class="hljs-string">'employees'</span>, { <span class="hljs-attr">id</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'managerId'</span>) },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">alias</span>: <span class="hljs-string">'managers'</span>, <span class="hljs-attr">columns</span>: [<span class="hljs-string">'name'</span>] }),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">directReports</span>: db.<span class="hljs-title function_">count</span>(<span class="hljs-string">'employees'</span>, { <span class="hljs-attr">managerId</span>: db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">'id'</span>) },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">alias</span>: <span class="hljs-string">'reports'</span> }),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "name") <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">2</span>::text, "lateral_directReports".<span class="hljs-keyword">result</span>, $<span class="hljs-number">3</span>::text, "lateral_lineManager".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "employees"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>("reports".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> "employees" <span class="hljs-keyword">AS</span> "reports"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> ("managerId" <span class="hljs-operator">=</span> "employees"."id")) <span class="hljs-keyword">AS</span> "lateral_directReports" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">4</span>::text, "name") <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> "employees" <span class="hljs-keyword">AS</span> "managers"</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> "employees"."managerId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  LIMIT $<span class="hljs-number">5</span>) <span class="hljs-keyword">AS</span> "lateral_lineManager" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">AS</span> "sq_employees"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"directReports"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"lineManager"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Anna"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"lineManager"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">null</span></span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"directReports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Beth"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"lineManager"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Anna"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"directReports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Charlie"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"lineManager"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Anna"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"directReports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Dougal"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"lineManager"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Beth"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"directReports"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>As usual, this is fully typed. If, for example, you were to forget that <code>directReports</code> is a count rather than an array of employees, VS Code would soon disabuse you.</p>
<h6 id="lateral-pass-through"><code>lateral</code> pass-through</h6>
<p>As previously mentioned, the <code>lateral</code> key can also take a single nested query shortcut. In this case, the result of the lateral query is promoted and passed directly through as the result of the parent query. This can be helpful when working with many-to-many relationships between tables.</p>
<p>For instance, let’s say we’ve got two tables, <code>photos</code> and <code>subjects</code>, where <code>subjects</code> holds data on the people who appear in the photos. This is a many-to-many relationship, since a photo can have many subjects and a subject can be in many photos. We model it with a third table, <code>subjectPhotos</code>.</p>
<p>Here are the tables:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "photos" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "photoId" <span class="hljs-type">int</span> <span class="hljs-keyword">PRIMARY</span> KEY GENERATED ALWAYS <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "url" text <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "subjects"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "subjectId" <span class="hljs-type">int</span> <span class="hljs-keyword">PRIMARY</span> KEY GENERATED ALWAYS <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "name" text <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "subjectPhotos"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "subjectId" <span class="hljs-type">int</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">REFERENCES</span> "subjects"("subjectId")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "photoId" <span class="hljs-type">int</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">REFERENCES</span> "photos"("photoId")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, <span class="hljs-keyword">CONSTRAINT</span> "userPhotosUnique" <span class="hljs-keyword">UNIQUE</span> ("subjectId", "photoId")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span></code></pre>
<p>Insert some data:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [alice, bobby, cathy] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'subjects'</span>, [</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">name</span>: <span class="hljs-string">'Alice'</span> }, { <span class="hljs-attr">name</span>: <span class="hljs-string">'Bobby'</span> }, { <span class="hljs-attr">name</span>: <span class="hljs-string">'Cathy'</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [photo1, photo2, photo3] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'photos'</span>, [</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    { <span class="hljs-attr">url</span>: <span class="hljs-string">'photo1.jpg'</span> }, { <span class="hljs-attr">url</span>: <span class="hljs-string">'photo2.jpg'</span> }, { <span class="hljs-attr">url</span>: <span class="hljs-string">'photo3.jpg'</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'subjectPhotos'</span>, [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">subjectId</span>: alice.<span class="hljs-property">subjectId</span>, <span class="hljs-attr">photoId</span>: photo1.<span class="hljs-property">photoId</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">subjectId</span>: alice.<span class="hljs-property">subjectId</span>, <span class="hljs-attr">photoId</span>: photo2.<span class="hljs-property">photoId</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">subjectId</span>: bobby.<span class="hljs-property">subjectId</span>, <span class="hljs-attr">photoId</span>: photo2.<span class="hljs-property">photoId</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">subjectId</span>: cathy.<span class="hljs-property">subjectId</span>, <span class="hljs-attr">photoId</span>: photo1.<span class="hljs-property">photoId</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">subjectId</span>: cathy.<span class="hljs-property">subjectId</span>, <span class="hljs-attr">photoId</span>: photo3.<span class="hljs-property">photoId</span> },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "subjects" ("name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>), ($<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("subjects".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Bobby"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Cathy"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Bobby"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Cathy"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "photos" ("url")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>), ($<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("photos".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"photo1.jpg"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"photo2.jpg"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"photo3.jpg"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo1.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo2.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo3.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "subjectPhotos" ("photoId", "subjectId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>), ($<span class="hljs-number">5</span>, $<span class="hljs-number">6</span>), ($<span class="hljs-number">7</span>, $<span class="hljs-number">8</span>), ($<span class="hljs-number">9</span>, $<span class="hljs-number">10</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("subjectPhotos".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>And now query for all photos with their subjects:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> photos = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'photos'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">subjects</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'subjectPhotos'</span>, { <span class="hljs-attr">photoId</span>: db.<span class="hljs-title function_">parent</span>() }, {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">lateral</span>: db.<span class="hljs-title function_">selectExactlyOne</span>(<span class="hljs-string">'subjects'</span>, { <span class="hljs-attr">subjectId</span>: db.<span class="hljs-title function_">parent</span>() })</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    })</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("photos".<span class="hljs-operator">*</span>) <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "lateral_subjects".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "photos"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">SELECT</span> "lateral_passthru".<span class="hljs-keyword">result</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">FROM</span> "subjectPhotos"</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-keyword">SELECT</span> to_jsonb ("subjects".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-keyword">FROM</span> "subjects"</span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-keyword">WHERE</span> ("subjectId" <span class="hljs-operator">=</span> "subjectPhotos"."subjectId")</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        LIMIT $<span class="hljs-number">2</span>) <span class="hljs-keyword">AS</span> "lateral_passthru" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">WHERE</span> ("photoId" <span class="hljs-operator">=</span> "photos"."photoId")) <span class="hljs-keyword">AS</span> "sq_subjectPhotos") <span class="hljs-keyword">AS</span> "lateral_subjects" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span>) <span class="hljs-keyword">AS</span> "sq_photos"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"subjects"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo1.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjects"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Cathy"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo2.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjects"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Bobby"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"url"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"photo3.jpg"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"photoId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"subjects"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Cathy"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"subjectId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>Note that the <code>subjects</code> subquery is passed directly to the <code>lateral</code> option of the <code>subjectPhotos</code> query, and its result is therefore passed straight through, effectively overwriting the <code>subjectPhotos</code> query result. That’s fine, since the intermediate <code>subjectPhotos</code> results would be effectively just noise here, in the form of duplicate copies of the <code>photoId</code> and <code>subjectId</code> primary keys.</p>
<p>Note also that when a lateral join matches on the same column name in the parent and child tables, you can omit that column name from the call to <code>parent()</code>. In other words, <code>{ columnName: db.parent() }</code> is equivalent to <code>{ columnName: db.parent('columnName') }</code>.</p>
<p>When you pass a nested query directly to the <code>lateral</code> option of a parent query, nothing else is returned from that parent query. For this reason, specifying <code>columns</code> or <code>extras</code> on the parent query would have no effect, and trying to do so will give you a type error.</p>
<h6 id="limitations">Limitations</h6>
<p>There are still a few limitations to type inference for nested queries. First, there’s no check that your joins make sense (column types and <code>REFERENCES</code> relationships are not exploited in the <code>Whereable</code> term). Second, we need to manually specify <code>selectExactlyOne</code> instead of <code>selectOne</code> when we know that a join will always produce a result — such as when the relevant foreign key is <code>NOT NULL</code> and has a <code>REFERENCES</code> constraint —&nbsp;which in principle might be inferred for us. Third, note that <code>strictNullChecks</code> (or <code>strict</code>) must be turned on in <code>tsconfig.json</code>, or nothing gets added to the return type.</p>
<p>Nevertheless, this is a handy, flexible — but still transparent and zero-abstraction — way to generate and run complex join queries.</p>
<h5 id="extras"><code>extras</code></h5>
<p>The <code>extras</code> option allows us to include additional result keys that don’t directly replicate the columns of our tables. That can be a computed quantity, such as a geographical distance via <a href="https://postgis.net/">PostGIS</a>, or it can be a simple column alias.</p>
<p>As is discussed above for <code>insert</code>, the <code>extras</code> option takes a mapping of property names to column names and/or <code>sql</code> template strings (i.e. <code>SQLFragments</code>). The <code>RunResult</code> type variable of any template string is significant, since it is passed through to the result type.</p>
<p>Let’s see <code>extras</code> in use, with an example that shows too how the <code>lateral</code> option can go well beyond simply matching a foreign key to a primary key.</p>
<p>Take this new table:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> EXTENSION postgis;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "stores"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "name" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "geom" GEOMETRY <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> );</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> INDEX "storesGeomIdx" <span class="hljs-keyword">ON</span> "stores" <span class="hljs-keyword">USING</span> gist("geom");</span></code></pre>
<p>Insert some new stores:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-title function_">gbPoint</span> = (<span class="hljs-params">mEast: <span class="hljs-built_in">number</span>, mNorth: <span class="hljs-built_in">number</span></span>) =&gt;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  db.<span class="hljs-property">sql</span><span class="hljs-string">`ST_SetSRID(ST_Point(<span class="hljs-subst">${db.param(mEast)}</span>, <span class="hljs-subst">${db.param(mNorth)}</span>), 27700)`</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [brighton] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'stores'</span>, [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Brighton'</span>, <span class="hljs-attr">geom</span>: <span class="hljs-title function_">gbPoint</span>(<span class="hljs-number">530590</span>, <span class="hljs-number">104190</span>) },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'London'</span>, <span class="hljs-attr">geom</span>: <span class="hljs-title function_">gbPoint</span>(<span class="hljs-number">534930</span>, <span class="hljs-number">179380</span>) },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Edinburgh'</span>, <span class="hljs-attr">geom</span>: <span class="hljs-title function_">gbPoint</span>(<span class="hljs-number">323430</span>, <span class="hljs-number">676130</span>) },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Newcastle'</span>, <span class="hljs-attr">geom</span>: <span class="hljs-title function_">gbPoint</span>(<span class="hljs-number">421430</span>, <span class="hljs-number">563130</span>) },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">name</span>: <span class="hljs-string">'Exeter'</span>, <span class="hljs-attr">geom</span>: <span class="hljs-title function_">gbPoint</span>(<span class="hljs-number">288430</span>, <span class="hljs-number">92130</span>) },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "stores" ("geom", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> (ST_SetSRID (ST_Point ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), <span class="hljs-number">27700</span>), $<span class="hljs-number">3</span>), (ST_SetSRID (ST_Point ($<span class="hljs-number">4</span>, $<span class="hljs-number">5</span>), <span class="hljs-number">27700</span>), $<span class="hljs-number">6</span>), (ST_SetSRID (ST_Point ($<span class="hljs-number">7</span>, $<span class="hljs-number">8</span>), <span class="hljs-number">27700</span>), $<span class="hljs-number">9</span>), (ST_SetSRID (ST_Point ($<span class="hljs-number">10</span>, $<span class="hljs-number">11</span>), <span class="hljs-number">27700</span>), $<span class="hljs-number">12</span>), (ST_SetSRID (ST_Point ($<span class="hljs-number">13</span>, $<span class="hljs-number">14</span>), <span class="hljs-number">27700</span>), $<span class="hljs-number">15</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("stores".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">530590</span><span class="hljs-punctuation">,</span> <span class="hljs-number">104190</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Brighton"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">534930</span><span class="hljs-punctuation">,</span> <span class="hljs-number">179380</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"London"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">323430</span><span class="hljs-punctuation">,</span> <span class="hljs-number">676130</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Edinburgh"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">421430</span><span class="hljs-punctuation">,</span> <span class="hljs-number">563130</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Newcastle"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">288430</span><span class="hljs-punctuation">,</span> <span class="hljs-number">92130</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Exeter"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"geom"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"crs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"EPSG:27700"</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Point"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"coordinates"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">530590</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">104190</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Brighton"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"geom"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"crs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"EPSG:27700"</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Point"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"coordinates"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">534930</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">179380</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"London"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"geom"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"crs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"EPSG:27700"</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Point"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"coordinates"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">323430</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">676130</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Edinburgh"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"geom"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"crs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"EPSG:27700"</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Point"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"coordinates"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">421430</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">563130</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Newcastle"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"geom"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"crs"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">"properties"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"EPSG:27700"</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Point"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"coordinates"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">288430</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-number">92130</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Exeter"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>And now query my local store (Brighton) plus its three nearest alternatives, with their distances in metres, using PostGIS’s index-aware <a href="https://postgis.net/docs/geometry_distance_knn.html"><code>&lt;-&gt; operator</code></a>:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  distance = db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">stores</span>.<span class="hljs-property">SQL</span>, <span class="hljs-built_in">number</span>&gt;<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-string">"geom"</span>}</span> &lt;-&gt; <span class="hljs-subst">${db.parent(<span class="hljs-string">"geom"</span>)}</span>`</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  localStore = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">selectOne</span>(<span class="hljs-string">'stores'</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span> }, {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">columns</span>: [<span class="hljs-string">'name'</span>],</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">lateral</span>: {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">alternatives</span>: db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'stores'</span>, { <span class="hljs-attr">id</span>: dc.<span class="hljs-title function_">ne</span>(db.<span class="hljs-title function_">parent</span>(<span class="hljs-string">"id"</span>)) }, {</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">alias</span>: <span class="hljs-string">'nearby'</span>,</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">columns</span>: [<span class="hljs-string">'id'</span>],</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">extras</span>: { </span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          distance,  <span class="hljs-comment">// &lt;-- i.e. distance: distance, referring to the SQLFragment just defined</span></span>
<span class="indent-line" style="padding-left: 14ch; text-indent: -14ch;">          <span class="hljs-attr">storeName</span>: <span class="hljs-string">"name"</span>,  <span class="hljs-comment">// &lt;-- a simple alias for the name column</span></span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        },  </span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">order</span>: { <span class="hljs-attr">by</span>: distance, <span class="hljs-attr">direction</span>: <span class="hljs-string">'ASC'</span> },</span>
<span class="indent-line" style="padding-left: 12ch; text-indent: -12ch;">        <span class="hljs-attr">limit</span>: <span class="hljs-number">3</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      })</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    }</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "name") <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">2</span>::text, "lateral_alternatives".<span class="hljs-keyword">result</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "stores"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> <span class="hljs-keyword">LATERAL</span> (</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">3</span>::text, "id") <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">4</span>::text, "geom" <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> "stores"."geom", $<span class="hljs-number">5</span>::text, "name") <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">FROM</span> "stores" <span class="hljs-keyword">AS</span> "nearby"</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">WHERE</span> (("id" <span class="hljs-operator">&lt;&gt;</span> "stores"."id"))</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> "geom" <span class="hljs-operator">&lt;</span><span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> "stores"."geom" <span class="hljs-keyword">ASC</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    LIMIT $<span class="hljs-number">6</span>) <span class="hljs-keyword">AS</span> "sq_nearby") <span class="hljs-keyword">AS</span> "lateral_alternatives" <span class="hljs-keyword">ON</span> <span class="hljs-literal">true</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">7</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT $<span class="hljs-number">8</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"name"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"alternatives"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"id"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"distance"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"storeName"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Brighton"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"alternatives"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"distance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">75315.14920651754</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"storeName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"London"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"distance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">242460.11878245047</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"storeName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Exeter"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"distance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">471743.3933824617</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">"storeName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Newcastle"</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>The <code>extras</code> option requires <code>strictNullChecks</code> (or <code>strict</code>) to be turned on in <code>tsconfig.json</code>.</p>
<h5 id="groupby-and-having"><code>groupBy</code> and <code>having</code></h5>
<p>The <code>groupBy</code> and <code>having</code> options work as you’d probably expect. The value of <code>groupBy</code> should be a single <code>Column</code>, a <code>Column[]</code> array or a <code>SQLFragment</code>. The value of <code>having</code> should be a <code>Whereable</code> or <code>SQLFragment</code>.</p>
<p>You’ll likely want to use these in conjunction with <a href="#columns"><code>columns</code></a> and <a href="#extras"><code>extras</code></a>. To take a rather contrived example:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> multiBookAuthorTitleData = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">columns</span>: [<span class="hljs-string">'authorId'</span>],</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">extras</span>: {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">titleCount</span>: db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, <span class="hljs-built_in">number</span>&gt;<span class="hljs-string">`count(<span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span>)`</span>,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">titleChars</span>: db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>, <span class="hljs-built_in">number</span>&gt;<span class="hljs-string">`sum(char_length(<span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span>))`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">groupBy</span>: <span class="hljs-string">'authorId'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">having</span>: db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">books</span>.<span class="hljs-property">SQL</span>&gt;<span class="hljs-string">`count(<span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span>) &gt; 1`</span>,</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> jsonb_build_object($<span class="hljs-number">1</span>::text, "authorId") <span class="hljs-operator">||</span> jsonb_build_object($<span class="hljs-number">2</span>::text, <span class="hljs-built_in">count</span>("title"), $<span class="hljs-number">3</span>::text, <span class="hljs-built_in">sum</span>(<span class="hljs-keyword">char_length</span>("title"))) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> "authorId"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">count</span>("title") <span class="hljs-operator">&gt;</span> <span class="hljs-number">1</span>) <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"authorId"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"titleCount"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"titleChars"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"titleChars"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">65</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"titleCount"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"titleChars"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">49</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"titleCount"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<h5 id="distinct"><code>distinct</code></h5>
<p>The <code>distinct</code> option, unsurprisingly, adds <a href="https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT"><code>DISTINCT</code></a> to your query. If <code>true</code> it adds only <code>DISTINCT</code>. If a single <code>Column</code>, a <code>Column[]</code> array, or a <code>SQLFragment</code>, it adds the appropriate <code>DISTINCT ON (/* ... */)</code> clause.</p>
<p>For instance:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books1 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, { <span class="hljs-attr">distinct</span>: <span class="hljs-literal">true</span> }).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books2 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, { <span class="hljs-attr">distinct</span>: <span class="hljs-string">'title'</span> }).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books3 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, { <span class="hljs-attr">distinct</span>: [<span class="hljs-string">'title'</span>, <span class="hljs-string">'authorId'</span>] }).<span class="hljs-title function_">run</span>(pool),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  books4 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, db.<span class="hljs-property">all</span>, { <span class="hljs-attr">distinct</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`upper(<span class="hljs-subst">${<span class="hljs-string">"title"</span>}</span>)`</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> ( <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> ( <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> <span class="hljs-keyword">ON</span> ("title")</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> ( <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> <span class="hljs-keyword">ON</span> ("title", "authorId")</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> ( <span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">DISTINCT</span> <span class="hljs-keyword">ON</span> (<span class="hljs-built_in">upper</span>("title")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">) to_jsonb ("books".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "books") <span class="hljs-keyword">AS</span> "sq_books"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"A Brief History of Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"My Brief History"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.463945+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Northern Lights"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603082+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Amber Spyglass"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603732+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1003</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Curious Incident of the Dog in the Night-Time"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.605245+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Subtle Knife"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:22:52.603558+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"title"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"The Universe in a Nutshell"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"authorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"createdAt"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2024-06-23T15:24:10.467708+01:00"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>(For the <code>DISTINCT ON</code> variants, you should really use <a href="#order-limit-and-offset"><code>order</code></a> too, or you don’t really know which rows you’ll get).</p>
<h5 id="lock"><code>lock</code></h5>
<p>The <code>lock</code> option defines a <a href="https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE">locking clause</a>. It takes a <code>SelectLockingOptions</code> object or <code>SelectLockingOptions[]</code> array, defined as:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SelectLockingOptions</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">for</span>: <span class="hljs-string">'UPDATE'</span> | <span class="hljs-string">'NO KEY UPDATE'</span> | <span class="hljs-string">'SHARE'</span> | <span class="hljs-string">'KEY SHARE'</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">of</span>?: <span class="hljs-title class_">Table</span> | <span class="hljs-title class_">Table</span>[];</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  wait?: <span class="hljs-string">'NOWAIT'</span> | <span class="hljs-string">'SKIP LOCKED'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<p>(And yes, this allows for arbitrary locking scenarios that a shorcut <code>select</code> can’t yet need).</p>
<p>A couple of examples:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authors1 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">"authors"</span>, db.<span class="hljs-property">all</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lock</span>: { <span class="hljs-attr">for</span>: <span class="hljs-string">"NO KEY UPDATE"</span> } </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> authors2 = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">"authors"</span>, db.<span class="hljs-property">all</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">lock</span>: { <span class="hljs-attr">for</span>: <span class="hljs-string">"UPDATE"</span>, <span class="hljs-attr">of</span>: <span class="hljs-string">"authors"</span>, <span class="hljs-attr">wait</span>: <span class="hljs-string">"NOWAIT"</span> } </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">NO</span> KEY <span class="hljs-keyword">UPDATE</span>) <span class="hljs-keyword">AS</span> "sq_authors"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "authors"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">OF</span> "authors" NOWAIT) <span class="hljs-keyword">AS</span> "sq_authors"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1000</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Philip Pullman"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1001</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Mark Haddon"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1002</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Louis Sachar"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Gabriel Garcia Marquez"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Douglas Adams"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Jane Austen"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Joseph Conrad"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Stephen Hawking"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"isLiving"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/shortcuts.ts#L649">Source code »</a></div>
<h4 id="count-avg-sum-min-and-max"><code>count</code>, <code>avg</code>, <code>sum</code>, <code>min</code> and <code>max</code></h4>
<p>The <code>count</code>, <code>avg</code>, <code>sum</code>, <code>min</code> and <code>max</code> functions generate <code>SELECT</code> queries that apply the relevant aggregate to matching rows, and so each return a <code>SQLFragment&lt;number&gt;</code>.</p>
<p>They’re used in a very similar way to <code>select</code>, like this:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> numberOfAuthors = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">count</span>(<span class="hljs-string">'authors'</span>, db.<span class="hljs-property">all</span>).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>("authors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "authors"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-number">8</span></span></code></pre>
</div>
<h4 id="jsonselectable"><code>JSONSelectable</code></h4>
<p>Since the shortcut functions build on Postgres’ JSON support, their return values are typed <code>JSONSelectable</code> rather than the <code>Selectable</code> you’d get back from a manual query (this would not in fact be a hard requirement for all shortcuts, but in the interests of consistency it does apply to all of them).</p>
<p><code>JSONSelectable</code>s differ from <code>Selectable</code>s in that some data types that would normally be converted to native JavaScript representations by <code>pg</code> are instead returned in the string format produced by the Postgres <code>to_json</code> function. Namely:</p>
<ul>
<li>Since JSON has no native date representation, columns returned as <code>Date</code> values in a <code>Selectable</code> are returned as string values in a <code>JSONSelectable</code>. These strings are assigned appropriate template types: <code>DateString</code>, <code>TimeString</code>, <code>TimeTzString</code>, <code>TimestampString</code> and <code>TimestampTzString</code>. For example, <code>DateString</code> is defined as <code>`${number}-${number}-${number}`</code>. Two helper functions, <code>toDate()</code> and <code>toString()</code>, are provided to convert between JavaScript’s <code>Date</code> and some of these string representations, while maintaining nullability and forcing explicit treatment of timezones. For example:</li>
</ul>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d1 = db.<span class="hljs-title function_">toDate</span>(<span class="hljs-string">'2012-06-01T12:34:00Z'</span>),  <span class="hljs-comment">// TimestampTzString -&gt; Date</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d2 = db.<span class="hljs-title function_">toDate</span>(<span class="hljs-string">'2012-06-01T00:00'</span>, <span class="hljs-string">'local'</span>),  <span class="hljs-comment">// TimestampString (Europe/London) -&gt; Date</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d3 = db.<span class="hljs-title function_">toDate</span>(<span class="hljs-string">'2012-06-01'</span>, <span class="hljs-string">'UTC'</span>),  <span class="hljs-comment">// DateString (UTC) -&gt; Date</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d4 = db.<span class="hljs-title function_">toDate</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() &lt; <span class="hljs-number">0.5</span> ? <span class="hljs-literal">null</span> : <span class="hljs-string">'2012-10-09T02:34Z'</span>) <span class="hljs-comment">// TimestampTzString | null -&gt; Date | null;</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>({ d1, d2, d3, d4 });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s1 = db.<span class="hljs-title function_">toString</span>(d1, <span class="hljs-string">'timestamptz'</span>),  <span class="hljs-comment">// Date -&gt; TimestampTzString</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s2 = db.<span class="hljs-title function_">toString</span>(d2, <span class="hljs-string">'timestamp:local'</span>),  <span class="hljs-comment">// Date -&gt; TimestampString (Europe/London)</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s3 = db.<span class="hljs-title function_">toString</span>(d3, <span class="hljs-string">'date:UTC'</span>),  <span class="hljs-comment">// Date -&gt; DateString (UTC)</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s4 = db.<span class="hljs-title function_">toString</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() &lt; <span class="hljs-number">0.5</span> ? <span class="hljs-literal">null</span> : d4, <span class="hljs-string">'timestamptz'</span>); <span class="hljs-comment">// Date | null -&gt; TimestampTzString | null</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>({ s1, s2, s3, s4 });</span></code></pre><div class="sqlstuff">
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d1: 2012-06-01T12:34:00.000Z,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d2: 2012-05-31T23:00:00.000Z,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d3: 2012-06-01T00:00:00.000Z,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  d4: null</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s1: '2012-06-01T12:34:00.000Z',</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s2: '2012-06-01T00:00:00.000',</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s3: '2012-06-01',</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  s4: null</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
</div>
<ul>
<li>
<p><code>bigint</code>/<code>int8</code> and <code>numeric</code>/<code>decimal</code> columns are returned as string values (of template string type <code>`${number}`</code>) in a <code>Selectable</code>, but as numbers in a <code>JSONSelectable</code>. This point is discussed in the next section.</p>
</li>
<li>
<p><code>bytea</code> columns are returned as <code>ByteArrayString</code>, defined as <code>`\\x{string}`</code>. A <code>toBuffer()</code> function is provided for use with these. For performance and memory reasons, this should not be used for large objects:&nbsp;in that case, consider something like <a href="https://www.npmjs.com/package/pg-large-object">pg-large-object</a> instead.</p>
</li>
<li>
<p>Range types such as <code>numrange</code> also get template string types. (Unfortunately, unlike standalone time/date types, which are always returned in ISO8601 format in JSON, time/date bounds in ranges are formatted according to Postgres’ current <code>DateStyle</code> setting, so can’t be typed more specifically than <code>string</code>).</p>
</li>
</ul>
<p>If you’re using a time/date library such as <a href="https://moment.github.io/luxon/">Luxon</a> or <a href="https://momentjs.com/">Moment</a>, use Zapatos’ <code>strict</code> function to roll your own time/date conversions, returning (and inferring) <code>null</code> on <code>null</code> input. For example:</p>
<pre class="language-typescript runnable"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">DateTime</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'luxon'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// conversions to and from Luxon's DateTime</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> toDateTime = db.<span class="hljs-property">strict</span>&lt;db.<span class="hljs-property">TimestampTzString</span>, <span class="hljs-title class_">DateTime</span>&gt;(<span class="hljs-title class_">DateTime</span>.<span class="hljs-property">fromISO</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> toTsTzString = db.<span class="hljs-title function_">strict</span>(<span class="hljs-function">(<span class="hljs-params">d: DateTime</span>) =&gt;</span> d.<span class="hljs-title function_">toISO</span>() <span class="hljs-keyword">as</span> db.<span class="hljs-property">TimestampTzString</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// db.strict handles null input both for type inference and at runtime</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> tsTz = <span class="hljs-string">'1989-11-09T18:53:00.000+01:00'</span> <span class="hljs-keyword">as</span> db.<span class="hljs-property">TimestampTzString</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> tsTzOrNull = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() &lt; <span class="hljs-number">0.5</span> ? tsTz : <span class="hljs-literal">null</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> dt1 = <span class="hljs-title function_">toDateTime</span>(<span class="hljs-literal">null</span>);  <span class="hljs-comment">// dt1: null</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> dt2 = <span class="hljs-title function_">toDateTime</span>(tsTz);  <span class="hljs-comment">// dt2: DateTime</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> dt3 = <span class="hljs-title function_">toDateTime</span>(tsTzOrNull);  <span class="hljs-comment">// dt3: DateTime | null</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> alsoTsTz = <span class="hljs-title function_">toTsTzString</span>(dt2);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>({ dt1, dt2, dt3, alsoTsTz });</span></code></pre><div class="sqlstuff">
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  dt1: null,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  dt2: DateTime {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ts: 626637180000,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    _zone: SystemZone {},</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    loc: Locale {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      locale: 'en-GB',</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      numberingSystem: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      outputCalendar: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      intl: 'en-GB',</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      weekdaysCache: [Object],</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      monthsCache: [Object],</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      meridiemCache: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      eraCache: {},</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      specifiedLocale: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      fastNumbersCached: null</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    invalid: null,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    weekData: null,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    c: {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      year: 1989,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      month: 11,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      day: 9,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      hour: 17,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      minute: 53,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      second: 0,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      millisecond: 0</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    o: -0,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    isLuxonDateTime: true</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  dt3: DateTime {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    ts: 626637180000,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    _zone: SystemZone {},</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    loc: Locale {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      locale: 'en-GB',</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      numberingSystem: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      outputCalendar: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      intl: 'en-GB',</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      weekdaysCache: [Object],</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      monthsCache: [Object],</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      meridiemCache: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      eraCache: {},</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      specifiedLocale: null,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      fastNumbersCached: null</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    invalid: null,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    weekData: null,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    c: {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      year: 1989,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      month: 11,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      day: 9,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      hour: 17,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      minute: 53,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      second: 0,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      millisecond: 0</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    },</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    o: -0,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    isLuxonDateTime: true</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  alsoTsTz: '1989-11-09T17:53:00.000+00:00'</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
</div>
<h5 id="custom-json-parsing-for-bigint-and-numeric">Custom JSON parsing for <code>bigint</code> and <code>numeric</code></h5>
<p>All numeric values are returned as ordinary number literals in Postgres’ JSON types. That means <code>bigint</code>/<code>int8</code> values could exceed <code>Number.MAX_SAFE_INTEGER</code> (and might become different integers in the process), while <code>numeric</code>/<code>decimal</code> values could overflow <code>Number.MAX_VALUE</code> or lose precision. I’ve written about this issue in more detail <a href="https://neon.tech/blog/parsing-json-from-postgres-in-js">elsewhere</a>.</p>
<p>For this reason, if your database includes any <code>bigint</code>/<code>int8</code> or <code>numeric</code>/<code>decimal</code> columns, a warning will be printed at schema-generation time (since Zapatos version 6.3).</p>
<p>To address this issue:</p>
<ul>
<li>
<p>Set <code>"customJSONParsingForLargeNumbers": true</code> in the schema-generation config in <code>zapatosconfig.json</code>. This switches the TypeScript types for these columns in <code>JSONSelectable</code>s from <code>number</code> to <code>number | `${number}`</code>. It also suppresses the warning.</p>
</li>
<li>
<p>Be sure to call <code>db.enableCustomJSONParsingForLargeNumbers(pg)</code> in your code before running any queries. This switches node-postgres’s JSON parsing to use the <a href="https://github.com/jawj/json-custom-numbers">json-custom-numbers</a> package, and return as strings any values that aren’t representable as a JS number.</p>
</li>
</ul>
<p>When using these <code>number | `${number}`</code> values in code, you will likely want to convert integers to <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt"><code>BigInt</code></a> and decimals to a third-party decimal format such as <a href="https://github.com/MikeMcl/big.js">big.js</a> at the earliest opportunity.</p>
<p>Here’s an example:</p>
<pre class="language-typescript runnable"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool.js'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pg <span class="hljs-keyword">from</span> <span class="hljs-string">'pg'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> <span class="hljs-title class_">Big</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'big.js'</span>;  <span class="hljs-comment">// third-party arbitrary-precision library</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// note: set `"customJSONParsingForLargeNumbers": true` in zapatosconfig.json</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">db.<span class="hljs-title function_">enableCustomJSONParsingForLargeNumbers</span>(pg);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// bigints</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> bigints = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">"bigints"</span>, db.<span class="hljs-property">all</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">order</span>: { <span class="hljs-attr">by</span>: <span class="hljs-string">"bigintValue"</span>, <span class="hljs-attr">direction</span>: <span class="hljs-string">"ASC"</span> } }).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> { <span class="hljs-attr">bigintValue</span>: raw } <span class="hljs-keyword">of</span> bigints) {  <span class="hljs-comment">// raw is number | `${number}`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> <span class="hljs-built_in">number</span> = <span class="hljs-title class_">Number</span>(raw);  <span class="hljs-comment">// DON'T do this: may become a different integer</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> <span class="hljs-built_in">bigint</span> = <span class="hljs-title class_">BigInt</span>(raw);  <span class="hljs-comment">// do this instead</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'raw:'</span>, raw, <span class="hljs-string">'/ as Number:'</span>, <span class="hljs-built_in">number</span>, <span class="hljs-string">'/ as BigInt:'</span>, <span class="hljs-built_in">bigint</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// Note that numbers above `Number.MAX_SAFE_INTEGER` are still returned as</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// numbers *if* that doesn't change them. For example, 9007199254740993 is</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// returned as a string (because it would become 9007199254740992), but</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// 9007199254740992 is safely returned as an ordinary number, even though</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// it's `Number.MAX_SAFE_INTEGER + 1`.</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// numerics</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> numerics = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">"numerics"</span>, db.<span class="hljs-property">all</span>, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">order</span>: { <span class="hljs-attr">by</span>: <span class="hljs-string">"numericValue"</span>, <span class="hljs-attr">direction</span>: <span class="hljs-string">"ASC"</span> } }).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> { <span class="hljs-attr">numericValue</span>: raw } <span class="hljs-keyword">of</span> numerics) {  <span class="hljs-comment">// raw is number | `${number}`</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> <span class="hljs-built_in">number</span> = <span class="hljs-title class_">Number</span>(raw);  <span class="hljs-comment">// DON'T do this: may overflow or lose precision</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> bigdec = <span class="hljs-title class_">Big</span>(raw);  <span class="hljs-comment">// do this instead</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'raw:'</span>, raw, <span class="hljs-string">'/ as Number:'</span>, <span class="hljs-built_in">number</span>, <span class="hljs-string">'/ as Big:'</span>, bigdec);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> pool.<span class="hljs-title function_">end</span>();</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("bigints".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "bigints"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> "bigintValue" <span class="hljs-keyword">ASC</span>) <span class="hljs-keyword">AS</span> "sq_bigints"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bigintValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">9007199254740991</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bigintValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">9007199254740992</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"bigintValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"9007199254740993"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 9007199254740991 / as Number: 9007199254740991 / as BigInt: 9007199254740991n</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 9007199254740992 / as Number: 9007199254740992 / as BigInt: 9007199254740992n</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 9007199254740993 / as Number: 9007199254740992 / as BigInt: 9007199254740993n</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("numerics".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "numerics"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> "numericValue" <span class="hljs-keyword">ASC</span>) <span class="hljs-keyword">AS</span> "sq_numerics"</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"numericValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"1.2345678901234566"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"numericValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1.2345678901234567</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"numericValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1.7e+308</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"numericValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 1.2345678901234566 / as Number: 1.2345678901234567 / as Big: 1.2345678901234566</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 1.2345678901234567 / as Number: 1.2345678901234567 / as Big: 1.2345678901234567</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 1.7e+308 / as Number: 1.7e+308 / as Big: 1.7e+308</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">raw: 180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 / as Number: Infinity / as Big: 1.8e+308</span></code></pre>
</div>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/transaction.ts#L81">Source code »</a></div>
<h3 id="transaction"><code>transaction</code></h3>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">IsolationLevel</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// these are the only meaningful values in Postgres: </span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// see https://www.postgresql.org/docs/11/sql-set-transaction.html</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">Serializable</span> = <span class="hljs-string">"SERIALIZABLE"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">RepeatableRead</span> = <span class="hljs-string">"REPEATABLE READ"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">ReadCommitted</span> = <span class="hljs-string">"READ COMMITTED"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">SerializableRO</span> = <span class="hljs-string">"SERIALIZABLE, READ ONLY"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">RepeatableReadRO</span> = <span class="hljs-string">"REPEATABLE READ, READ ONLY"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">ReadCommittedRO</span> = <span class="hljs-string">"READ COMMITTED, READ ONLY"</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title class_">SerializableRODeferrable</span> = <span class="hljs-string">"SERIALIZABLE, READ ONLY, DEFERRABLE"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> transaction&lt;T, M <span class="hljs-keyword">extends</span> <span class="hljs-title class_">IsolationLevel</span>&gt;(</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">txnClientOrQueryable</span>: <span class="hljs-title class_">Queryable</span> | <span class="hljs-title class_">TxnClient</span>&lt;<span class="hljs-title class_">IsolationSatisfying</span>&lt;M&gt;&gt;,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">isolationLevel</span>: M,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">callback</span>: <span class="hljs-function">(<span class="hljs-params">client: TxnClient&lt;IsolationSatisfying&lt;M&gt;&gt;</span>) =&gt;</span> <span class="hljs-title class_">Promise</span>&lt;T&gt;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">): <span class="hljs-title class_">Promise</span>&lt;T&gt;</span></code></pre>
<p>The <code>transaction</code> helper takes a <code>pg.Pool</code> or already-connected <code>pg.Client</code> instance, an isolation mode, and an <code>async</code> callback function (it can also take an existing <code>TxnClient</code> instead, but <a href="#transaction-sharing">we’ll cover that later</a>). It then proceeds as follows:</p>
<ul>
<li>Issue a <code>BEGIN TRANSACTION</code>.</li>
<li>Call the callback, passing it a database client (checked out from the pool, if that’s what was given).</li>
<li>If a serialization error is thrown, try again after a <a href="#run-time-configuration">configurable</a> random delay, a <a href="#run-time-configuration">configurable</a> number of times.</li>
<li>If any other error is thrown, issue a <code>ROLLBACK</code>, release the database client (if it’s one it checked out earlier), and re-throw the error.</li>
<li>Otherwise <code>COMMIT</code> the transaction, release the database client (if it’s one it checked out earlier), and return the callback’s result.</li>
</ul>
<p>As is implied above, for <code>REPEATABLE READ</code> or <code>SERIALIZABLE</code> isolation modes the callback could be called several times. It’s therefore important that it doesn’t have any non-database-related side-effects (i.e. don’t, say, bill your customer’s credit card from this function).</p>
<p>We already saw <a href="#transactions">one <code>transaction</code> example</a>. Here’s another, adapted from <a href="https://www.cockroachlabs.com/docs/stable/demo-serializable.html">CockroachDB’s write-up on <code>SERIALIZABLE</code></a>.</p>
<p>We have a table of <code>doctors</code>, and a table of their assigned <code>shifts</code>.</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "doctors"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" SERIAL <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "name" TEXT <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> );</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "shifts" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "day" <span class="hljs-type">DATE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "doctorId" <span class="hljs-type">INTEGER</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">REFERENCES</span> "doctors"("id")</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, <span class="hljs-keyword">PRIMARY</span> KEY ("day", "doctorId") );</span></code></pre>
<p>We populate those tables with two doctors and two days’ shifts:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'doctors'</span>, [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Annabel'</span> }, </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Brian'</span> },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'shifts'</span>, [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">day</span>: <span class="hljs-string">'2020-12-24'</span>, <span class="hljs-attr">doctorId</span>: <span class="hljs-number">1</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">day</span>: <span class="hljs-string">'2020-12-24'</span>, <span class="hljs-attr">doctorId</span>: <span class="hljs-number">2</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">day</span>: <span class="hljs-string">'2020-12-25'</span>, <span class="hljs-attr">doctorId</span>: <span class="hljs-number">1</span> },</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  { <span class="hljs-attr">day</span>: <span class="hljs-string">'2020-12-25'</span>, <span class="hljs-attr">doctorId</span>: <span class="hljs-number">2</span> },</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "doctors" ("id", "name")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("doctors".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Annabel"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"Brian"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Annabel"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"name"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Brian"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "shifts" ("day", "doctorId")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>, $<span class="hljs-number">4</span>), ($<span class="hljs-number">5</span>, $<span class="hljs-number">6</span>), ($<span class="hljs-number">7</span>, $<span class="hljs-number">8</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-24"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"2020-12-24"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-24"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-24"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>The important business logic is that there must always be <em>at least one doctor</em> on shift. Now let’s say both doctors happen at the same moment to request leave for 25 December.</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-title function_">requestLeaveForDoctorOnDay</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params">doctorId: <span class="hljs-built_in">number</span>, day: db.DateString</span>) =&gt;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  db.<span class="hljs-title function_">transaction</span>(pool, db.<span class="hljs-property">IsolationLevel</span>.<span class="hljs-property">Serializable</span>, <span class="hljs-keyword">async</span> txnClient =&gt; {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">const</span> otherDoctorsOnShift = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">count</span>(<span class="hljs-string">'shifts'</span>, {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-attr">doctorId</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> != <span class="hljs-subst">${db.param(doctorId)}</span>`</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      day,</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    }).<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">if</span> (otherDoctorsOnShift === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">deletes</span>(<span class="hljs-string">'shifts'</span>, { day, doctorId }).<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [leaveBookedForAnnabel, leaveBookedForBrian] = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">// in practice, these requests would come from different front-ends</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title function_">requestLeaveForDoctorOnDay</span>(<span class="hljs-number">1</span>, <span class="hljs-string">'2020-12-25'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-title function_">requestLeaveForDoctorOnDay</span>(<span class="hljs-number">2</span>, <span class="hljs-string">'2020-12-25'</span>),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">]);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Leave booked for:</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  Annabel – <span class="hljs-subst">${leaveBookedForAnnabel}</span></span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  Brian – <span class="hljs-subst">${leaveBookedForBrian}</span>`</span>);</span></code></pre><div class="sqlstuff">
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "shifts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("day" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">AND</span> ("doctorId" <span class="hljs-operator">!=</span> $<span class="hljs-number">2</span>))</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "shifts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("day" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">AND</span> ("doctorId" <span class="hljs-operator">!=</span> $<span class="hljs-number">2</span>))</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-number">1</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> "shifts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("day" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">AND</span> "doctorId" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-number">1</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> "shifts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("day" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">AND</span> "doctorId" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">2</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"day"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"doctorId"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="transactionlog"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction rollback (code 40001) on attempt 1 of 5, retrying in 98ms</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="transactionlog"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Retrying transaction, attempt 2 of 5</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>("shifts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> "shifts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("day" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">AND</span> ("doctorId" <span class="hljs-operator">!=</span> $<span class="hljs-number">2</span>))</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"2020-12-25"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">1</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-number">0</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Leave booked for:</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  Annabel – false</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  Brian – true</span></code></pre>
</div>
<p>Expanding the results, we see that one of the requests is retried and then fails — as it must to retain one doctor on shift — thanks to the <code>SERIALIZABLE</code> isolation. <code>REPEATABLE READ</code>, which is one isolation level weaker, wouldn’t help here.</p>
<h4 id="transaction-isolation-shortcuts">Transaction isolation shortcuts</h4>
<p>To help save keystrokes and line noise, there is a family of transaction shortcut functions named after each isolation mode. For example, instead of:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">transaction</span>(pool, db.<span class="hljs-property">IsolationLevel</span>.<span class="hljs-property">Serializable</span>, <span class="hljs-keyword">async</span> txnClient =&gt; { <span class="hljs-comment">/* ... */</span> });</span></code></pre>
<p>You can use the equivalent:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-keyword">async</span> txnClient =&gt; { <span class="hljs-comment">/* ... */</span> });</span></code></pre>
<h4 id="isolationsatisfying-generic"><code>IsolationSatisfying</code> generic</h4>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">IsolationSatisfying</span>&lt;T <span class="hljs-keyword">extends</span> <span class="hljs-title class_">IsolationLevel</span>&gt; = {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [<span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">Serializable</span>]: <span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">Serializable</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [<span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">RepeatableRead</span>]: <span class="hljs-title class_">IsolationSatisfying</span>&lt;<span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">Serializable</span>&gt; | <span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">RepeatableRead</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">/* ... */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}[T];</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">TxnClientForSerializable</span> = <span class="hljs-title class_">TxnClient</span>&lt;<span class="hljs-title class_">IsolationSatisfying</span>&lt;<span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">Serializable</span>&gt;&gt;;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> <span class="hljs-title class_">TxnClientForRepeatableRead</span> = <span class="hljs-title class_">TxnClient</span>&lt;<span class="hljs-title class_">IsolationSatisfying</span>&lt;<span class="hljs-title class_">IsolationLevel</span>.<span class="hljs-property">RepeatableRead</span>&gt;&gt;;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">/* ... */</span></span></code></pre>
<p>If you find yourself passing transaction clients around, you may find the <code>IsolationSatisfying</code> generic useful. For example, if you type a <code>txnClient</code> argument to a function as <code>IsolationSatisfying&lt;IsolationLevel.RepeatableRead&gt;</code> —&nbsp;probably by using the alias type <code>TxnClientForRepeatableRead</code> — you can call it with a client having <code>IsolationLevel.Serializable</code> or <code>IsolationLevel.RepeatableRead</code> but not <code>IsolationLevel.ReadCommitted</code>.</p>
<h4 id="transaction-sharing">Transaction sharing</h4>
<p>A snag you might have encountered when using Postgres transactions is that, since transactions can’t be nested, it’s fiddly to break out SQL operations with cross-cutting isolation requirements into self-contained functions.</p>
<p>Recall the transaction example we began with: a <a href="#transactions">money transfer between two bank accounts</a>. We do this within a transaction, because we need atomicity: we must ensure that either balance A is increased <em>and</em> balance B is correspondingly reduced, or that neither thing happens.</p>
<p>But what if we want to combine some other operations within the same database transaction? Say we want to make two transfers, A to B and A to C, or have both fail. The <code>transferMoney</code> function we originally wrote uses a transaction helper to <code>BEGIN</code> and <code>COMMIT</code> its own transaction every time, so we can’t just call it twice.</p>
<p>For this reason, the <code>transaction</code> function — and its isolation-level shortcuts — can be passed either a plain <code>pg.Pool</code>/<code>pg.Client</code>, in which case they manage a transaction as decribed above, or an existing <code>TxnClient</code>. If they’re passed an existing <code>TxnClient</code>, they do no more than call the provided callback function with the provided client on the spot.</p>
<p>Let’s see how this helps. We’ll modify the <code>transferMoney</code> function to take a pool or transaction client as its last argument, and pass that straight to the <code>serializable</code> transaction function. (Note that we <em>could</em> give this last argument a default value of <code>pool</code>, but I find that way it’s too easy to accidentally issue queries outside of transactions).</p>
<p>With that done, we can now use <code>transferMoney</code> both for individual transfers, without worrying about transactions, and in combination with other operations, by taking charge of the transaction ourselves:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [accountA, accountB, accountC] = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  [{ <span class="hljs-attr">balance</span>: <span class="hljs-number">50</span> }, { <span class="hljs-attr">balance</span>: <span class="hljs-number">50</span> }, { <span class="hljs-attr">balance</span>: <span class="hljs-number">50</span> }]).<span class="hljs-title function_">run</span>(pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> <span class="hljs-title function_">transferMoney</span> = (<span class="hljs-params">sendingAccountId: <span class="hljs-built_in">number</span>, receivingAccountId: <span class="hljs-built_in">number</span>, amount: <span class="hljs-built_in">number</span>, txnClientOrPool: <span class="hljs-keyword">typeof</span> pool | db.TxnClientForSerializable</span>) =&gt;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  db.<span class="hljs-title function_">serializable</span>(txnClientOrPool, <span class="hljs-function"><span class="hljs-params">txnClient</span> =&gt;</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    db.<span class="hljs-title function_">update</span>(<span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">balance</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> - <span class="hljs-subst">${db.param(amount)}</span>`</span> },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">id</span>: sendingAccountId }).<span class="hljs-title function_">run</span>(txnClient),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    db.<span class="hljs-title function_">update</span>(<span class="hljs-string">'bankAccounts'</span>,</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">balance</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> + <span class="hljs-subst">${db.param(amount)}</span>`</span> },</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      { <span class="hljs-attr">id</span>: receivingAccountId }).<span class="hljs-title function_">run</span>(txnClient),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]));</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// single transfer, as before (but passing in `pool`)</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> <span class="hljs-title function_">transferMoney</span>(accountA.<span class="hljs-property">id</span>, accountB.<span class="hljs-property">id</span>, <span class="hljs-number">60</span>, pool);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">} <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">message</span>, <span class="hljs-string">'/'</span>, err.<span class="hljs-property">detail</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// multiple transfers, passing in an external transaction</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-function"><span class="hljs-params">txnClient</span> =&gt;</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>([</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title function_">transferMoney</span>(accountA.<span class="hljs-property">id</span>, accountB.<span class="hljs-property">id</span>, <span class="hljs-number">40</span>, txnClient),</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title function_">transferMoney</span>(accountA.<span class="hljs-property">id</span>, accountC.<span class="hljs-property">id</span>, <span class="hljs-number">40</span>, txnClient)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  ]));</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">} <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(err.<span class="hljs-property">message</span>, <span class="hljs-string">'/'</span>, err.<span class="hljs-property">detail</span>);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'bankAccounts'</span>, { <span class="hljs-attr">id</span>: dc.<span class="hljs-title function_">isIn</span>([accountA.<span class="hljs-property">id</span>, accountB.<span class="hljs-property">id</span>, accountC.<span class="hljs-property">id</span>]) }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "bankAccounts" ("balance")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>), ($<span class="hljs-number">2</span>), ($<span class="hljs-number">3</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">50</span><span class="hljs-punctuation">,</span> <span class="hljs-number">50</span><span class="hljs-punctuation">,</span> <span class="hljs-number">50</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">-</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">60</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">+</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">60</span><span class="hljs-punctuation">,</span> <span class="hljs-number">4</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">new row for relation "bankAccounts" violates check constraint "bankAccounts_balance_check" / Failing row contains (3, -10).</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">-</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">40</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">+</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">40</span><span class="hljs-punctuation">,</span> <span class="hljs-number">4</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">-</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">40</span><span class="hljs-punctuation">,</span> <span class="hljs-number">3</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">UPDATE</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  "bankAccounts"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SET</span> ("balance") <span class="hljs-operator">=</span> <span class="hljs-type">ROW</span> ("balance" <span class="hljs-operator">+</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">40</span><span class="hljs-punctuation">,</span> <span class="hljs-number">5</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">10</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">90</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">new row for relation "bankAccounts" violates check constraint "bankAccounts_balance_check" / Failing row contains (3, -30).</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">coalesce</span>(jsonb_agg(<span class="hljs-keyword">result</span>), <span class="hljs-string">'[]'</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> (</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">SELECT</span> to_jsonb ("bankAccounts".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">FROM</span> "bankAccounts"</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">WHERE</span> (("id" <span class="hljs-keyword">IN</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>, $<span class="hljs-number">3</span>)))) <span class="hljs-keyword">AS</span> "sq_bankAccounts"</span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">3</span><span class="hljs-punctuation">,</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span> <span class="hljs-number">5</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">5</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<p>If you expand the results you’ll see that both transactions fail, as intended.</p>
<p>Happily, the type system will prevent us from trying to pass <code>transferMoney</code> a database client associated with an insufficiently isolated transaction. If we were to substitute <code>db.serializable</code> with <code>db.repeatableRead</code> inside the second <code>try</code> block, TypeScript would complain.</p>
<div style="height: 1px; clear: both;"></div><div class="src-link"><a href="https://github.com/jawj/zapatos/blob/master/src/db/pgErrors.ts#L16">Source code »</a></div>
<h3 id="errors">Errors</h3>
<p>Zapatos provides a simple function to help you recognise and recover from errors thrown by <code>pg</code>.</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">function</span> <span class="hljs-title function_">isDatabaseError</span>(<span class="hljs-params">err: <span class="hljs-built_in">Error</span>, ...types: (keyof <span class="hljs-keyword">typeof</span> pgErrors)[]</span>): <span class="hljs-built_in">boolean</span>;</span></code></pre>
<p>You pass it your JS <code>Error</code> object, and one or more <a href="https://www.postgresql.org/docs/current/errcodes-appendix.html">Postgres error names</a>. It returns <code>true</code> if your error is a <code>pg</code> error of any of those kinds, and <code>false</code> otherwise.</p>
<p>It works with both general error class names and specific error names.</p>
<p>The general class names contain no underscore and correspond to the first two characters of a 5-character Postgres error code: for example, <code>ConnectionException</code>, which is all codes starting <code>08</code>.</p>
<p>The specific error names contain one underscore and correspond to a full 5-character code: for example, <code>ConnectionException_ProtocolViolation</code>, which is code <code>08P01</code>.</p>
<p>As one example, the <code>transaction</code> helper uses this function to catch serialization problems, like so:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-comment">/* start transaction, run queries, commit */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">} <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> sql<span class="hljs-string">`ROLLBACK`</span>.<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">if</span> (<span class="hljs-title function_">isDatabaseError</span>(err, <span class="hljs-string">"TransactionRollback_SerializationFailure"</span>, <span class="hljs-string">"TransactionRollback_DeadlockDetected"</span>)) {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-comment">/* wait a bit, then have another go */</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  } <span class="hljs-keyword">else</span> {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">throw</span> err;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<p>As another example, let’s say we’re assigning one octet of an IP address by using a <code>SERIAL</code> column. We want these to remain sequential up to 254, and then to start filling in any gaps created by deleted rows.</p>
<p>Here’s the table:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "users" </span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">( "id" <span class="hljs-type">int</span> GENERATED <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> <span class="hljs-keyword">PRIMARY</span> KEY</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "ipOctet" <span class="hljs-type">int</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">CHECK</span> ("ipOctet" <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">254</span>) GENERATED <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> (MAXVALUE <span class="hljs-number">254</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">, "friendlyName" text</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">);</span></code></pre>
<p>Behind the scenes, 253 rows have already been inserted. Let’s delete one so we can see the filling-in process in action:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">deletes</span>(<span class="hljs-string">'users'</span>, { <span class="hljs-attr">id</span>: <span class="hljs-number">123</span> }).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> "users"</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> ("id" <span class="hljs-operator">=</span> $<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("users".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-number">123</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">123</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"ipOctet"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">123</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"friendlyName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Charlie"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
</div>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import type</span> * <span class="hljs-keyword">as</span> s <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/schema'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">createUser</span>(<span class="hljs-params">friendlyName: <span class="hljs-built_in">string</span></span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">return</span> db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-keyword">async</span> txnClient =&gt; {</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">let</span> user;</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">try</span> {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span><span class="hljs-string">`SAVEPOINT "start"`</span>.<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      user = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'users'</span>, { friendlyName }).<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    } <span class="hljs-keyword">catch</span> (<span class="hljs-attr">err</span>: <span class="hljs-built_in">any</span>) {</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">if</span> (!db.<span class="hljs-title function_">isDatabaseError</span>(err, <span class="hljs-string">'DataException_SequenceGeneratorLimitExceeded'</span>)) <span class="hljs-keyword">throw</span> err;</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      </span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span><span class="hljs-string">`ROLLBACK TO "start"`</span>.<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">const</span> ipOctet = <span class="hljs-keyword">await</span> <span class="hljs-title function_">getFirstFreeIpOctet</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      <span class="hljs-keyword">if</span> (!ipOctet) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 10ch; text-indent: -10ch;">      user = <span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">'users'</span>, { friendlyName, ipOctet }).<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    }</span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-keyword">return</span> user;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">getFirstFreeIpOctet</span>(<span class="hljs-params">txnClient: db.TxnClientForSerializable</span>) {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> db.<span class="hljs-property">sql</span>&lt;s.<span class="hljs-property">users</span>.<span class="hljs-property">SQL</span>, [{ <span class="hljs-attr">octet</span>: <span class="hljs-built_in">number</span> }] | []&gt;<span class="hljs-string">`</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    SELECT gs.octet </span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    FROM generate_series(1, 254) AS gs(octet) </span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    LEFT JOIN <span class="hljs-subst">${<span class="hljs-string">"users"</span>}</span> AS u ON u.<span class="hljs-subst">${<span class="hljs-string">"ipOctet"</span>}</span> = gs.octet</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    WHERE u.<span class="hljs-subst">${<span class="hljs-string">"ipOctet"</span>}</span> IS NULL</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;"><span class="hljs-string">    ORDER BY gs.octet ASC LIMIT 1</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;"><span class="hljs-string">  `</span>.<span class="hljs-title function_">run</span>(txnClient);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">return</span> result[<span class="hljs-number">0</span>]?.<span class="hljs-property">octet</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> [alice, bob, cathy] = [</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> <span class="hljs-title function_">createUser</span>(<span class="hljs-string">'Alice'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> <span class="hljs-title function_">createUser</span>(<span class="hljs-string">'Bob'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">await</span> <span class="hljs-title function_">createUser</span>(<span class="hljs-string">'Cathy'</span>),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">];</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(alice, bob, cathy);</span></code></pre><div class="sqlstuff">
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SAVEPOINT</span> "start"</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "users" ("friendlyName")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("users".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Alice"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">254</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"ipOctet"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">254</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"friendlyName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Alice"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 0</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SAVEPOINT</span> "start"</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "users" ("friendlyName")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("users".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Bob"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span> <span class="hljs-keyword">TO</span> "start"</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> gs.octet</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> generate_series(<span class="hljs-number">1</span>, <span class="hljs-number">254</span>) <span class="hljs-keyword">AS</span> gs (octet)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> "users" <span class="hljs-keyword">AS</span> u <span class="hljs-keyword">ON</span> u. "ipOctet" <span class="hljs-operator">=</span> gs.octet</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> u. "ipOctet" <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> gs.octet <span class="hljs-keyword">ASC</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT <span class="hljs-number">1</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-attr">"octet"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">123</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">}</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "users" ("friendlyName", "ipOctet")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>, $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("users".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Bob"</span><span class="hljs-punctuation">,</span> <span class="hljs-number">123</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">256</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"ipOctet"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">123</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"friendlyName"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"Bob"</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 1</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">START</span> TRANSACTION ISOLATION LEVEL SERIALIZABLE</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SAVEPOINT</span> "start"</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "users" ("friendlyName")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> ($<span class="hljs-number">1</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("users".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"Cathy"</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ROLLBACK</span> <span class="hljs-keyword">TO</span> "start"</span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">SELECT</span> gs.octet</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">FROM</span> generate_series(<span class="hljs-number">1</span>, <span class="hljs-number">254</span>) <span class="hljs-keyword">AS</span> gs (octet)</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> "users" <span class="hljs-keyword">AS</span> u <span class="hljs-keyword">ON</span> u. "ipOctet" <span class="hljs-operator">=</span> gs.octet</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">WHERE</span> u. "ipOctet" <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NULL</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> gs.octet <span class="hljs-keyword">ASC</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">LIMIT <span class="hljs-number">1</span></span></code></pre>
<pre class="transactionid"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Transaction 2</span></code></pre>
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">COMMIT</span></span></code></pre>
<pre class="console"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">{ id: 254, ipOctet: 254, friendlyName: 'Alice' } { id: 256, ipOctet: 123, friendlyName: 'Bob' } null</span></code></pre>
</div>
<h3 id="utility-types">Utility types</h3>
<p>Zapatos provides a few over-arching types designed to help you comprehensively enumerate the objects in your database. All of these are literal string array types, in alphabetical order —&nbsp;e.g. <code>["myTable1", "myTable2", "myTable3", "otherSchema.myTable1"]</code>&nbsp;—&nbsp;and are as follows:</p>
<ul>
<li><code>AllSchemas</code>: schema names</li>
<li><code>AllBaseTables</code>: ordinary tables, originating from <code>CREATE TABLE</code></li>
<li><code>AllForeignTables</code>: foreign tables, originating from <code>CREATE FOREIGN TABLE</code></li>
<li><code>AllViews</code>: ordinary views, deriving from <code>CREATE VIEW</code></li>
<li><code>AllMaterializedViews</code>: materialized views, deriving from <code>CREATE MATERIALIZED VIEW</code></li>
<li><code>AllTablesAndViews</code>: all of the above combined</li>
</ul>
<p>These global types list all relevant objects across all schemas. Schema-specific namespaced variants are also available (except, of course, in the case of <code>AllSchemas</code>).</p>
<p>For example, all ordinary tables in the <code>public</code> schema are listed in <code>public.AllBaseTables</code> (this name is prefixed irrespective of the value of the <code>"unprefixedSchema"</code> config option). Or all views in a custom schema might be found under <code>myOtherSchema.AllViews</code>.</p>
<p>Zapatos also provides a number of type mappings allowing types to be accessed by table name, which are heavily used by the shortcut functions:</p>
<ul>
<li><code>SelectableForTable&lt;Table&gt;</code></li>
<li><code>JSONSelectableForTable&lt;Table&gt;</code></li>
<li><code>WhereableForTable&lt;Table&gt;</code></li>
<li><code>InsertableForTable&lt;Table&gt;</code></li>
<li><code>UpdatableForTable&lt;Table&gt;</code></li>
<li><code>UniqueIndexForTable&lt;Table&gt;</code></li>
<li><code>ColumnForTable&lt;Table&gt;</code></li>
<li><code>SQLForTable&lt;Table&gt;</code></li>
</ul>
<h3 id="run-time-configuration">Run-time configuration</h3>
<p>There are a few configuration options you can set at runtime:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Config</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">transactionAttemptsMax</span>: <span class="hljs-built_in">number</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">transactionRetryDelay</span>: { <span class="hljs-attr">minMs</span>: <span class="hljs-built_in">number</span>; <span class="hljs-attr">maxMs</span>: <span class="hljs-built_in">number</span> };</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">castArrayParamsToJson</span>: <span class="hljs-built_in">boolean</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">castObjectParamsToJson</span>: <span class="hljs-built_in">boolean</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  queryListener?(<span class="hljs-attr">query</span>: <span class="hljs-title class_">SQLQuery</span>, txnId?: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">void</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  resultListener?(<span class="hljs-attr">result</span>: <span class="hljs-built_in">any</span>, txnId?: <span class="hljs-built_in">number</span>, elapsedMs?: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">void</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  transactionListener?(<span class="hljs-attr">message</span>: <span class="hljs-built_in">string</span>, txnId?: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">void</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">SQLQuery</span> {</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">text</span>: <span class="hljs-built_in">string</span>;</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">values</span>: <span class="hljs-built_in">any</span>[];</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}</span></code></pre>
<p>Read the current values with <code>getConfig()</code> and set new values with <code>setConfig(newConfig: Partial&lt;Config&gt;)</code>.</p>
<ul>
<li>
<p><code>transactionAttemptsMax</code> determines how many times the <code>transaction</code> helper will try to execute a query in the face of serialization errors before giving up. It defaults to <code>5</code>.</p>
</li>
<li>
<p><code>transactionRetryDelay</code> determines the range within which the <code>transaction</code> helper will pick a random delay before each retry. It’s expressed in milliseconds and defaults to <code>{ minMs: 25, maxMs: 250 }</code>.</p>
</li>
<li>
<p><code>castArrayParamsToJson</code> and <code>castObjectParamsToJson</code> control whether <code>Parameter</code> objects containing arrays and objects, respectively, are to be automatically stringified and cast as Postgres <code>json</code> when interpolated into a query. Both default to <code>false</code>. See further discussion below.</p>
</li>
<li>
<p><code>queryListener</code> and <code>resultListener</code>, if set, are called from the <code>run</code> function, and receive the results of (respectively) compiling and then executing and transforming each query as their first argument. For queries within a transaction, they will be passed a unique numeric transaction ID as their second argument, to aid debugging. The <code>resultListener</code> receives a third argument, which is the time the query took (in ms).</p>
</li>
<li>
<p><code>transactionListener</code>, similarly, is called with messages about transaction retries, and associated transaction IDs.</p>
</li>
</ul>
<p>You might use one or more of the three listener functions to implement logging. For example, if you’re using the <a href="https://github.com/visionmedia/debug"><code>debug</code></a> library, you could do something like this:</p>
<pre class="language-typescript norun"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  queryDebug = <span class="hljs-title function_">debug</span>(<span class="hljs-string">'db:query'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  resultDebug = <span class="hljs-title function_">debug</span>(<span class="hljs-string">'db:result'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  txnDebug = <span class="hljs-title function_">debug</span>(<span class="hljs-string">'db:transaction'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  strFromTxnId = <span class="hljs-function">(<span class="hljs-params">txnId: <span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span></span>) =&gt;</span> txnId === <span class="hljs-literal">undefined</span> ? <span class="hljs-string">'-'</span> : <span class="hljs-title class_">String</span>(txnId);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">db.<span class="hljs-title function_">setConfig</span>({</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">queryListener</span>: <span class="hljs-function">(<span class="hljs-params">query, txnId</span>) =&gt;</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title function_">queryDebug</span>(<span class="hljs-string">`(%s) %s\n%o`</span>, <span class="hljs-title function_">strFromTxnId</span>(txnId), query.<span class="hljs-property">text</span>, query.<span class="hljs-property">values</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">resultListener</span>: <span class="hljs-function">(<span class="hljs-params">result, txnId, elapsedMs</span>) =&gt;</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title function_">resultDebug</span>(<span class="hljs-string">`(%s, %dms) %O`</span>, <span class="hljs-title function_">strFromTxnId</span>(txnId), elapsedMs?.<span class="hljs-title function_">toFixed</span>(<span class="hljs-number">1</span>), result),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">transactionListener</span>: <span class="hljs-function">(<span class="hljs-params">message, txnId</span>) =&gt;</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-title function_">txnDebug</span>(<span class="hljs-string">`(%s) %s`</span>, <span class="hljs-title function_">strFromTxnId</span>(txnId), message),</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">});</span></code></pre>
<p>These listeners are also used in generating the <em>Show generated SQL, results</em> elements of this documentation.</p>
<h4 id="casting-parameters-to-json">Casting <code>Parameters</code> to JSON</h4>
<p>There’s <a href="https://github.com/brianc/node-postgres/issues/2012">a longstanding gotcha in the <code>pg</code> module’s treatment of JSON parameters</a>. For <code>json</code> and <code>jsonb</code> values, you can pass a JavaScript object directly: <code>pg</code> automatically calls <code>JSON.stringify</code> for you behind the scenes. But try the same thing with a JavaScript array, and that doesn’t happen.</p>
<p>Using <code>pg</code> directly here, from Node:</p>
<pre><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">&gt; const pg = require('pg');</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">&gt; const pool = new pg.Pool(/* ... */);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">BoundPool { /* ... */ }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">&gt; pool.query('INSERT INTO jsontest (data) VALUES ($1)', [{ a: 1, b: 2, c: 3 }]);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Promise { &lt;pending&gt; }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">&gt; pool.query('INSERT INTO jsontest (data) VALUES ($1)', [[1, 2, 3]]);</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">Promise { &lt;pending&gt; }</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">&gt; (node:59488) UnhandledPromiseRejectionWarning: error: invalid input syntax for type json</span></code></pre>
<p>In this second case, <code>pg</code> can’t tell whether you’re trying to pass a JSON array or a native Postgres array, and it assumes the latter.</p>
<p>But if you know you’ll more often be passing JSON arrays than native Postgres arrays to <code>pg</code>, you can reverse this assumption by setting the Zapatos <code>castArrayParamsToJson</code> config option to <code>true</code>. When interpolating a <code>Parameter</code> instance (as returned by the <code>param</code> call) that wraps an array, Zapatos will then default to calling <code>JSON.stringify</code> on the array and casting it to <code>json</code>. Whether or not <code>castArrayParamsToJson</code> is set, you can always specify the desired stringifying and casting behaviour using the <a href="#paramvalue-any-cast-boolean--string-parameter">optional second argument to <code>param</code></a>.</p>
<p>To clarify, take this table:</p>
<pre class="language-sql"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> "arrays" ("jsonValue" jsonb, "textArray" text[]);</span></code></pre>
<p>When <code>castArrayParamsToJson</code> is <code>false</code> (the default):</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">db.<span class="hljs-title function_">setConfig</span>({ <span class="hljs-attr">castArrayParamsToJson</span>: <span class="hljs-literal">false</span> });  <span class="hljs-comment">// the default</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">"arrays"</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">jsonValue</span>: db.<span class="hljs-title function_">param</span>([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>], <span class="hljs-literal">true</span>),  <span class="hljs-comment">// true -&gt; manual cast to JSON</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">textArray</span>: [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>],</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "arrays" ("jsonValue", "textArray")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> (<span class="hljs-built_in">CAST</span>($<span class="hljs-number">1</span> <span class="hljs-keyword">AS</span> "json"), $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("arrays".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"[\"a\",\"b\",\"c\"]"</span><span class="hljs-punctuation">,</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span><span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span><span class="hljs-string">"c"</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"jsonValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"c"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"textArray"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"c"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>Or with <code>castArrayParamsToJson</code> set to <code>true</code>:</p>
<pre class="language-typescript runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">db.<span class="hljs-title function_">setConfig</span>({ <span class="hljs-attr">castArrayParamsToJson</span>: <span class="hljs-literal">true</span> });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">insert</span>(<span class="hljs-string">"arrays"</span>, { </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">jsonValue</span>: [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>],</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">textArray</span>: db.<span class="hljs-title function_">param</span>([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>], <span class="hljs-literal">false</span>),  <span class="hljs-comment">// false -&gt; prevent automatic cast to JSON</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">}).<span class="hljs-title function_">run</span>(pool);</span></code></pre><div class="sqlstuff">
<pre class="sqltext"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> "arrays" ("jsonValue", "textArray")</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-keyword">VALUES</span> (<span class="hljs-built_in">CAST</span>($<span class="hljs-number">1</span> <span class="hljs-keyword">AS</span> "json"), $<span class="hljs-number">2</span>)</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">RETURNING to_jsonb ("arrays".<span class="hljs-operator">*</span>) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">result</span></span></code></pre>
<pre class="sqlvalues"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">[</span><span class="hljs-string">"[\"a\",\"b\",\"c\"]"</span><span class="hljs-punctuation">,</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span><span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span><span class="hljs-string">"c"</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">]</span></span></code></pre>
<pre class="sqlresult"><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">{</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"jsonValue"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"c"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-attr">"textArray"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"a"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"b"</span><span class="hljs-punctuation">,</span></span>
<span class="indent-line" style="padding-left: 8ch; text-indent: -8ch;">    <span class="hljs-string">"c"</span></span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  <span class="hljs-punctuation">]</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-punctuation">}</span></span></code></pre>
</div>
<p>The <code>castObjectParamsToJson</code> option has a fairly similar effect. As seen above, <code>pg</code> already stringifies JavaScript objects, but it does not explicitly cast them to <code>json</code>, and instead passes them implicitly as <code>text</code>. This matters in the (probably rare) case that the parameter then requires an onward cast from <code>json</code> to another type.</p>
<p>For example, when working with recent PostGIS, casting <code>geometry</code> values to JSON produces handy <a href="https://geojson.org/">GeoJSON</a> output, and you can <a href="https://trac.osgeo.org/postgis/ticket/3687#comment:9">define your own cast</a> in the opposite direction too. However, when doing a GeoJSON <code>INSERT</code> into or <code>UPDATE</code> of a <code>geometry</code> column, the stringified JSON input parameter must be explicitly cast to JSON, otherwise it’s assumed to be <a href="https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry">Well-Known Text</a> and fails to parse. In Zapatos, you can specify the cast manually with the <a href="#paramvalue-any-cast-boolean--string-parameter">optional second argument to <code>param</code></a>, or you can set <code>castObjectParamsToJson</code> to <code>true</code>, and any JSON objects interpolated as a <code>Parameter</code> will be cast to <code>json</code> automatically.</p>
<h2 id="about-zapatos">About Zapatos</h2>
<h3 id="changes">Changes</h3>
<p>This change list is not comprehensive. For a complete version history, <a href="https://github.com/jawj/zapatos/commits/master">please see the commit list</a>.</p>
<h4 id="63">6.3</h4>
<p>Added <a href="#custom-json-parsing-for-bigint-and-numeric">support for large and precise numbers</a> — <code>bigint</code> and <code>numeric</code>/<code>decimal</code> —&nbsp;returned as JSON.</p>
<h4 id="62">6.2</h4>
<p>Return type for certain embedded <code>SQLFragments</code> <a href="https://github.com/jawj/zapatos/pull/150">now matches the <code>null</code> actually returned</a> (previously typed as <code>undefined</code>). Fix for <a href="https://github.com/jawj/zapatos/issues/161"><code>and</code> and <code>or</code> types</a>. Corrected <code>package.json</code> key order. Exporting <code>mapWithSeparator</code>.</p>
<h4 id="60">6.0</h4>
<p><em>Breaking change (if you use schemas)</em>: Thanks to generous sponsorship from <a href="https://www.seam.co">Seam</a>, Zapatos now supports schemas properly, prefixing the schema to table and enum names as necessary in the generated types. If you make use of Postgres schemas outside of the default <code>public</code> schema, you’ll need to add schema names in the appropriate places (TypeScript errors should show you where).</p>
<h4 id="50">5.0</h4>
<p><em>Breaking change</em>: The <code>AllTables</code> type (which somewhat arbitrarily included tables, foreign tables, and views, but not materialized views) is gone. In its place you’ll a variety of more and less specific <a href="#utility-types">utility types</a>. Also, <code>Updatable</code> and <code>Insertable</code> interfaces for tables and views that aren’t writable are now <code>{ [key: string]: never }</code> instead of <code>{}</code>.</p>
<h4 id="40">4.0</h4>
<p><em>Breaking change</em>: Various types in <code>JSONSelectable</code>s are now assigned template string types instead of plain old <code>string</code>, including date and time types, range types, and <code>bytea</code>. For example, pg’s <code>date</code> maps to a new type <code>DateString</code>, now defined as <code>`${number}-${number}-${number}`</code>, and <code>bytea</code> maps to <code>ByteArrayString</code>, which is <code>`\\x${string}`</code>.</p>
<p>This improves type safety, but some <code>string</code> values in existing code may need to be cast or replaced (e.g. with JS <code>Date</code> or <code>Buffer</code> instances). For the date and time types, new conversion functions <code>toDate</code> and <code>toString</code> are provided. Or you can roll your own conversions for date libraries such as Luxon and Moment with help from the new <code>strict</code> function.</p>
<p>TypeScript 4.1 is now required, and 4.3 is recommended.</p>
<h4 id="36">3.6</h4>
<p><em>New feature</em>: The <code>extras</code> option object can now take column names as well as <code>SQLFragments</code> as its values, <a href="https://github.com/jawj/zapatos/issues/80">enabling straightforward column aliasing</a> (similar to <code>SELECT "column" AS "aliasedColumn"</code>) in shortcut functions.</p>
<h4 id="35">3.5</h4>
<p><em>Minor features and fixes</em>: Added <code>upsert</code> option <code>reportAction: 'suppress'</code> as a workaround for <a href="https://github.com/jawj/zapatos/issues/74">issues with <code>xmax</code></a>. Made schema JSDoc comments <a href="#configure-it">optional</a>. Sorted <code>UniqueIndex</code> union types for <a href="https://github.com/jawj/zapatos/issues/76">stable ordering</a>. Moved to (mostly) separate type treatments across <code>Selectable</code>, <code>JSONSelectable</code>, <code>Whereable</code> etc., enabling <a href="https://github.com/jawj/zapatos/pull/68">proper treatment of <code>int8</code> and <code>Date</code></a> in and out of <code>JSONSelectable</code>.</p>
<h4 id="34">3.4</h4>
<p><em>New features + bugfix</em>: Added <code>upsert</code> shortcut support for <code>INSERT ... ON CONFLICT ... DO NOTHING</code> <a href="#insert--on-conflict--do-nothing">by passing an empty array as the <code>updateColumns</code> option</a> (and fixed <a href="https://github.com/jawj/zapatos/issues/71">a bug</a> where this, and certain other <code>upsert</code> queries, would generate invalid SQL). Added <a href="#updatevalues">a new <code>updateValues</code> option</a> for <code>upsert</code>. Added the <code>eq</code> condition helper, which was <a href="https://github.com/jawj/zapatos/issues/73">strangely missing</a>.</p>
<h4 id="33">3.3</h4>
<p><em>New feature</em>: Added an <code>updateColumns</code> option to <code>upsert</code>, enabling only a subset of columns to be updated on conflict.</p>
<p><em>New feature</em>: Added an <code>outExt</code> generation configuration key, to allow generating <code>.ts</code> files instead of <code>.d.ts</code> files.</p>
<h4 id="32">3.2</h4>
<p><em>New feature</em>: Types are now generated for materialized views as well as ordinary tables, <a href="https://github.com/jawj/zapatos/pull/55">thanks to @jtfell</a>.</p>
<h4 id="31">3.1</h4>
<p><em>New feature</em>: <a href="#lateral-pass-through">Pass-through <code>lateral</code> subqueries</a>, for querying <a href="https://github.com/jawj/zapatos/issues/48">many-to-many relationships</a>.</p>
<p><em>New feature</em>: <a href="https://github.com/jawj/zapatos/issues/25">As requested</a>, you can now manually exclude column keys from the <code>Insertable</code> and <code>Updatable</code> types, and make column keys optional in <code>Insertable</code> types, using a new <code>"columnOptions"</code> key in <code>zapatosconfig.json</code> or the corresponding <code>Config</code> object passed to <code>generate</code> (<a href="#configure-it">documentation</a>). On a similar note, <code>GENERATED ALWAYS</code> columns (both the <code>IDENTITY</code> and <code>STORED</code> varieties) are now automatically excluded from <code>Insertable</code> and <code>Updatable</code> types, since it’s an error to try to write to them.</p>
<h4 id="30">3.0</h4>
<p><em>Major breaking change</em>: Zapatos no longer copies its source to your source tree. In the long run, this is good news — now it’s just a normal module, updates won’t pollute your diffs, and so on. Thanks are due to <a href="https://github.com/eyelidlessness">@eyelidlessness</a> and <a href="https://github.com/jtfell">@jtfell</a>.</p>
<p>Right now, though, there’s a bit of work to do. When you run <code>npx zapatos</code> for the first time in version 3, you’ll see a message pointing out that you need to:</p>
<ul>
<li>
<p>Make sure Zapatos is filed under <code>"dependencies"</code> (not <code>"devDependencies"</code>) in <code>package.json</code></p>
</li>
<li>
<p>Remove the <code>"srcMode"</code> key, if present, from <code>zapatosconfig.json</code> or the config argument passed to <code>generate</code> (this instruction was added in 3.1).</p>
</li>
<li>
<p>Delete the old <code>zapatos/schema.ts</code> (but leave the new <code>zapatos/schema.d.ts</code>).</p>
</li>
<li>
<p>Delete the folder <code>zapatos/src</code>, and all its contents, which are old copied Zapatos source files.</p>
</li>
<li>
<p>Transfer any customised type declarations in <code>zapatos/custom</code> from the plain old <code>.ts</code> files to the new <code>.d.ts</code> files.</p>
</li>
<li>
<p>Delete all the plain old <code>.ts</code> files in <code>zapatos/custom</code>, including <code>index.ts</code>.</p>
</li>
<li>
<p>Ensure all the <code>.d.ts</code> files in <code>zapatos</code> are picked up by your TypeScript configuration (e.g. check the <code>"files"</code> or <code>"include"</code> keys in <code>tsconfig.json</code>).</p>
</li>
<li>
<p>If you use <code>ts-node</code> or <code>node -r ts-node/register</code> to run your project, ensure you pass the <code>--files</code> option (<code>ts-node</code> only) or set <code>TS_NODE_FILES=true</code> (either case).</p>
</li>
<li>
<p>Make the following changes to your imports (you can use VS Code’s ‘Replace in Files’ command for this, just remember to toggle Regular Expressions on):</p>
</li>
</ul>
<pre><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">1) Change:  import * as zapatos from 'zapatos'</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   To:      import * as zapatos from 'zapatos/generate'</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Search:  ^(\s*import[^"']*['"])zapatos(["'])</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Replace: $1zapatos/generate$2</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">2) Change:  import * as db from './path/to/zapatos/src'</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   To:      import * as db from 'zapatos/db'</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Search:  ^(\s*import[^"']*['"])[^"']*/zapatos/src(["'])</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Replace: $1zapatos/db$2</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;">3) Change:  import * as s from './path/to/zapatos/schema'</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   To:      import type * as s from 'zapatos/schema'</span>
<span class="indent-line" style="padding-left: 23ch; text-indent: -23ch;">                   ^^^^</span>
<span class="indent-line" style="padding-left: 23ch; text-indent: -23ch;">                   be sure to import type, not just import</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Search:  ^(\s*import\s*)(type\s*)?([^"']*['"])[^"']+/(zapatos/schema["'])</span>
<span class="indent-line" style="padding-left: 7ch; text-indent: -7ch;">   Replace: $1type $3$4</span></code></pre>
<p><em>Newly documented feature</em>: the <code>isDatabaseError</code> function <a href="#errors">is now documented</a>.</p>
<h4 id="20">2.0</h4>
<p><em>New feature</em>: new <code>returning</code> and <code>extras</code> options on <code>insert</code>, <code>update</code>, <code>upsert</code> and <code>deletes</code> queries. These behave like the <code>columns</code> and <code>extras</code> options on <code>select</code>.</p>
<p><em>Breaking change</em>: the optional last argument to <code>upsert</code> is now an options object, when previously it was a list of columns that should not be overwritten with <code>null</code> in the case of an <code>UPDATE</code>. That column list can now be passed via a <code>noNullUpdateColumns</code> key on the new options object.</p>
<h4 id="10">1.0</h4>
<p><em>New feature</em>: <a href="#transaction-sharing">transaction sharing support</a>. Also, for queries within a transaction, a unique numeric transaction ID is now passed as a second argument to the query/result/transaction listeners, to aid debugging.</p>
<p><em>Breaking change</em>: some transaction-related objects have been renamed (hence the jump in <a href="https://semver.org/">major version</a> to 1.0).</p>
<ul>
<li>The <code>Isolation</code> enum becomes <code>IsolationLevel</code>.</li>
<li>The <code>TxnSatisfying</code> namespace becomes an <code>IsolationSatisfying&lt;T extends IsolationLevel&gt;</code> generic type. So, for example, <code>TxnSatisfying.Serializable</code> is rewritten as <code>IsolationSatisfying&lt;IsolationLevel.Serializable&gt;</code>.</li>
</ul>
<p>Because these are a bit of a mouthful, there are new shortcuts for <code>TxnClient</code>, which is the context you’ll mainly want to use them in. For example, <code>TxnClientForSerializable</code> is an alias for <code>TxnClient&lt;IsolationSatisfying&lt;IsolationLevel.Serializable&gt;&gt;</code>.</p>
<h4 id="0157">0.1.57</h4>
<p><em>New features</em>: <a href="https://github.com/jawj/zapatos/blob/master/src/db/conditions.ts">condition helpers</a> for use within <code>Whereables</code>, and isolation level-specific <a href="#transaction-isolation-shortcuts">transaction shortcuts</a>.</p>
<p>Condition helpers let you rewrite query conditions like these:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> { conditions <span class="hljs-keyword">as</span> dc } <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> </span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  date = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(<span class="hljs-string">'1989-11-09T18:53:00+0100'</span>),</span>
<span class="indent-line" style="padding-left: 6ch; text-indent: -6ch;">  authorIds = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query1a = db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">createdAt</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> &gt;= <span class="hljs-subst">${db.param(date)}</span>`</span> });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// can be rewritten as</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query1b = db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">createdAt</span>: dc.<span class="hljs-title function_">after</span>(date) });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query2a = db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">authorId</span>: db.<span class="hljs-property">sql</span><span class="hljs-string">`<span class="hljs-subst">${db.self}</span> IN (<span class="hljs-subst">${db.vals(authorIds)}</span>)`</span> });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// can be rewritten as</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">const</span> query2b = db.<span class="hljs-title function_">select</span>(<span class="hljs-string">'books'</span>, { <span class="hljs-attr">authorId</span>: dc.<span class="hljs-title function_">isIn</span>(authorIds) });</span></code></pre>
<p>New transaction shortcuts per isolation level let you rewrite transactions like this one:</p>
<pre class="language-typescript noresult runnable"><code class="imports"><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> db <span class="hljs-keyword">from</span> <span class="hljs-string">'zapatos/db'</span>;</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">import</span> pool <span class="hljs-keyword">from</span> <span class="hljs-string">'./pgPool'</span>;</span></code><code><span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">transaction</span>(pool, db.<span class="hljs-property">IsolationLevel</span>.<span class="hljs-property">Serializable</span>, <span class="hljs-keyword">async</span> txnClient =&gt; { <span class="hljs-comment">/* ... */</span> });</span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-comment">// can be rewritten as</span></span>
<span class="indent-line" style="padding-left: 4ch; text-indent: -4ch;"><span class="hljs-keyword">await</span> db.<span class="hljs-title function_">serializable</span>(pool, <span class="hljs-keyword">async</span> txnClient =&gt; { <span class="hljs-comment">/* ... */</span> });</span></code></pre>
<h3 id="this-documentation">This documentation</h3>
<p>This document is created from a <a href="https://github.com/jawj/zapatos-docs/">separate repository</a>. All generated SQL has been funnelled through <a href="https://github.com/darold/pgFormatter">pgFormatter</a> for easier reading.</p>
<h3 id="fixes-feature-and-contributions">Fixes, feature and contributions</h3>
<p>If you’re asking for or contributing new work, my response is likely to reflect these principles:</p>
<p><strong>Correct, consistent, comprehensible.</strong>  I’m pretty likely to accept pull requests that fix bugs or improve readability or consistency without any major trade-offs. I’ll also do my best to respond with timely fixes to clear, minimal test cases that demonstrate unambiguous bugs.</p>
<p><strong>Small is beautiful.</strong>  I’m less likely to accept pull requests for features that significantly complicate the code base either to address niche use-cases or to eke out minor performance gains that are likely swamped by network and database latencies.</p>
<p><strong>Scratching my own itch.</strong>  I’m unlikely to put a lot of free effort into new features I don’t currently need.</p>
<p><strong>Sponsorship or consultancy.</strong> If you’d like to discuss <a href="https://github.com/sponsors/jawj">sponsoring work on the project</a>, or possible consultancy, please <a href="http://mackerron.com">get in touch</a>.</p>
<h3 id="sponsors">Sponsors</h3>
<p>Many thanks to <a href="https://www.seam.co">Seam</a> for sponsoring proper multi-schema support.</p>
<h3 id="whats-next">What’s next</h3>
<p>The roadmap includes:</p>
<ul>
<li>
<p><strong>Tests.</strong>  The proprietary server API that’s the original consumer of this library, over at <a href="https://www.psyt.co.uk">Psychological Technologies</a>, has a test suite that exercises most of the code base at least a little. Nevertheless, a proper test suite is still kind of indispensable. It should test not just returned values but also inferred types — which is a little fiddly.</p>
</li>
<li>
<p><strong>Alternative install mechanism.</strong> Older versions of Zapatos copied key source files into your source tree instead of using ambient type declarations. Not everyone liked that approach, but it did enable some advanced use-cases, such as interfacing with multiple databases. This approach may return as a configurable option.</p>
</li>
</ul>
<p>More speculative nice-to-haves would include:</p>
<ul>
<li><strong>More complete typing of <code>lateral</code> queries.</strong>  It would be great to make use of foreign key relationships and suchlike in generated types and the shortcut functions that make use of them.</li>
</ul>
<h3 id="alternatives">Alternatives</h3>
<p>You may find <a href="https://phiresky.github.io/blog/2020/sql-libs-for-typescript/">this excellent overview of TypeScript SQL libraries</a> useful.</p>
<h3 id="licence">Licence</h3>
<p>This software is released under the <a href="https://opensource.org/licenses/mit-license.php">MIT licence</a>.</p>
<p>Copyright © 2020 — 2023 George MacKerron</p>
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
<p>THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
<p><a href="https://translate.google.com/#view=home&amp;op=translate&amp;sl=es&amp;tl=en&amp;text=zapatos"><img src="zapatos.jpg" width="175" alt="Zapatos = shoes" style="margin-top: 60px; border: none;"></a></p>
</div>
        <script src="docs.js"></script>
      
    
  </body></html>